From aca1b0bcf67d80c0cca8df5e0aa9bf4f11e71e8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Dec 2023 08:38:39 +0000 Subject: [PATCH] deploy: e25724b09b0af46de41b358a068c50fe83c33f39 --- 404.html | 4 ++-- About/index.html | 4 ++-- Appendix/Vector-fill/index.html | 4 ++-- Basics/Basics/index.html | 4 ++-- Basics/Stamp/index.html | 4 ++-- Basics/Vanilla/index.html | 4 ++-- Introduction/index.html | 4 ++-- Tessellation/index.html | 4 ++-- assets/js/e7f20846.4d519ff4.js | 1 - assets/js/e7f20846.5a3c2c8d.js | 1 + .../js/{runtime~main.b299749b.js => runtime~main.82d580af.js} | 2 +- category/appendix/index.html | 4 ++-- category/basics/index.html | 4 ++-- index.html | 4 ++-- 14 files changed, 24 insertions(+), 24 deletions(-) delete mode 100644 assets/js/e7f20846.4d519ff4.js create mode 100644 assets/js/e7f20846.5a3c2c8d.js rename assets/js/{runtime~main.b299749b.js => runtime~main.82d580af.js} (89%) diff --git a/404.html b/404.html index 7286eff..bbec50b 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | Brush Rendering Tutorial - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/About/index.html b/About/index.html index 5723658..311c895 100644 --- a/About/index.html +++ b/About/index.html @@ -4,7 +4,7 @@ About | Brush Rendering Tutorial - + @@ -40,7 +40,7 @@ That's why I want to guide and inspire more researchers and engineers to work on them during my academic career. To begin with, I've created this tutorial series to help everybody learn my research. I'm looking forward to assisting my collaborators working on the same research topic in the future.

- + \ No newline at end of file diff --git a/Appendix/Vector-fill/index.html b/Appendix/Vector-fill/index.html index fa333ae..80faccd 100644 --- a/Appendix/Vector-fill/index.html +++ b/Appendix/Vector-fill/index.html @@ -4,7 +4,7 @@ Pre-introduction to Vector Fill | Brush Rendering Tutorial - + @@ -23,7 +23,7 @@ algorithms to locate a point better than the naive solution. To dive deeper, you may try using the CGAL 2D arrangement library and learn the arrangement constructed from 2D polylines, which is slightly different with a naive polygon mesh.

I hope this "2D mesh metaphor" will help you better understand the problems.

- + \ No newline at end of file diff --git a/Basics/Basics/index.html b/Basics/Basics/index.html index f5508ca..a24cfbf 100644 --- a/Basics/Basics/index.html +++ b/Basics/Basics/index.html @@ -4,7 +4,7 @@ Basics | Brush Rendering Tutorial - + @@ -33,7 +33,7 @@ I will elaborate on how to render this stroke in the next section. Feel free to change any other parts of the code as long as the function returns the position and radius array correctly.

Loading...

Blender Grease Pencil team has developed a lot of novel tools to edit this polyline with radius representation. Remember to check out if you were a researcher and want to develop novel techniques for digital painting.

- + \ No newline at end of file diff --git a/Basics/Stamp/index.html b/Basics/Stamp/index.html index 4b1dabc..cf2d607 100644 --- a/Basics/Stamp/index.html +++ b/Basics/Stamp/index.html @@ -4,7 +4,7 @@ Stamp | Brush Rendering Tutorial - + @@ -80,7 +80,7 @@ Then we get an interpolated radius value rpr_p in the fragment shader. You can prove this rpr_p is the stroke radius value across the current pixel as the figure shows. We assume the whole stroke is uni-radius and program the shader with rpr_p.

Loading...

I basically cannot perceive the deformation caused by the trick at the first glance.

- + \ No newline at end of file diff --git a/Basics/Vanilla/index.html b/Basics/Vanilla/index.html index 155ffa8..7d19584 100644 --- a/Basics/Vanilla/index.html +++ b/Basics/Vanilla/index.html @@ -4,7 +4,7 @@ Vanilla | Brush Rendering Tutorial - + @@ -74,7 +74,7 @@ It rarely happens for the geometry data comes from a 2D canvas. Modern tablets are stable enough to avoid generating this ill data. But it is common in 3D, I will introduce the solution together with 3D stroke rendering.

- + \ No newline at end of file diff --git a/Introduction/index.html b/Introduction/index.html index f2ef859..63976c2 100644 --- a/Introduction/index.html +++ b/Introduction/index.html @@ -4,7 +4,7 @@ Introduction | Brush Rendering Tutorial - + @@ -30,7 +30,7 @@ You will find "vertex.glsl" and "fragment.glsl" for vertex and fragment shader code. Whether they are hidden or shown will depend on the context.

Supplementary contents

Dropdown tab
Some contents are hidden inside a dropdown tab like this. They are complementary to the main contents. Feel free to skip them.

Citation

@inproceedings{Ciallo2023,
author = {Ciao, Shen and Wei, Li-Yi},
title = {Ciallo: The next-Generation Vector Paint Program},
year = {2023},
isbn = {9798400701436},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3587421.3595418},
doi = {10.1145/3587421.3595418},
booktitle = {ACM SIGGRAPH 2023 Talks},
articleno = {67},
numpages = {2},
keywords = {Digital painting, stylized stroke, arrangement, vector graphics. coloring, graphics processing unit (GPU)},
location = {Los Angeles, CA, USA},
series = {SIGGRAPH '23}
}
Research Tip

To demonstrate your research work about brush rendering, select vector drawings have variable radius or pen pressure data. Regular vector drawing datasets don't contain them.

- + \ No newline at end of file diff --git a/Tessellation/index.html b/Tessellation/index.html index 03e5d7e..d57fc7d 100644 --- a/Tessellation/index.html +++ b/Tessellation/index.html @@ -4,7 +4,7 @@ Tessellation-based Rendering | Brush Rendering Tutorial - + @@ -13,7 +13,7 @@ You can find them in several papers and online articles.

Paper and article list

They may inspire some approaches to optimize the performance of the articulated algorithms in the future. To learn about them, I would recommend starting with Rye Terrell's Instanced Line Rendering, whose methods are simple enough to learn.

As for this article, I will compare the tessellation-based algorithms with articulated algorithms in detail.

WIP

spoiler

The key benefit of the articulated is its self-overlapping in acute angle, which is critical for brush strokes and digital painting.

- + \ No newline at end of file diff --git a/assets/js/e7f20846.4d519ff4.js b/assets/js/e7f20846.4d519ff4.js deleted file mode 100644 index e23ab61..0000000 --- a/assets/js/e7f20846.4d519ff4.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunkbrush_stroke_tutorial=self.webpackChunkbrush_stroke_tutorial||[]).push([[342],{1410:(a,e,t)=>{const s=t(7694),n=t(3618),m={title:"Brush Rendering Tutorial",tagline:"Learn brush stroke rendering.",favicon:"img/favicon.png",url:"https://shenciao.github.io",baseUrl:"/brush-rendering-tutorial/",organizationName:"ShenCiao",projectName:"brush-rendering-tutorial",onBrokenLinks:"throw",onBrokenMarkdownLinks:"warn",i18n:{defaultLocale:"en",locales:["en"]},presets:[["classic",{docs:{remarkPlugins:[t(3545)],rehypePlugins:[t(1167)],routeBasePath:"/",sidebarPath:6679,editUrl:"https://github.com/ShenCiao/brush-rendering-tutorial/tree/main"},blog:!1,theme:{customCss:2295}}]],themeConfig:{colorMode:{disableSwitch:!0},image:"img/vanilla-stroke.png",navbar:{title:"Brush Rendering Tutorial",logo:{alt:"logo",src:"img/vanilla-stroke.png"},items:[{type:"docSidebar",sidebarId:"tutorialSidebar",position:"right",label:"Tutorial"},{href:"https://github.com/ShenCiao/brush-stroke-tutorial",label:"GitHub",position:"right"}]},footer:{style:"light",copyright:`Copyright \xa9 ${(new Date).getFullYear()} Brush Rendering Tutorial by Shen Ciao, under CC BY-SA 4.0 License`},prism:{theme:s,darkTheme:n},docs:{sidebar:{hideable:!0}},stylesheets:[{href:"https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css",type:"text/css",integrity:"sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM",crossorigin:"anonymous"}]},plugins:["raw-loaders"],trailingSlash:!0};a.exports=m},6679:a=>{a.exports={tutorialSidebar:[{type:"autogenerated",dirName:"."}]}},5034:(a,e,t)=>{"use strict";t.d(e,{r:()=>l});var s=t(7462),n=t(7294);const m={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"[",close:"]"},{open:"{",close:"}"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},p={tokenPostfix:".glsl",defaultToken:"invalid",keywords:["const","uniform","break","continue","do","for","while","if","else","switch","case","in","out","inout","true","false","invariant","discard","return","sampler2D","samplerCube","sampler3D","struct","radians","degrees","sin","cos","tan","asin","acos","atan","pow","sinh","cosh","tanh","asinh","acosh","atanh","exp","log","exp2","log2","sqrt","inversesqrt","abs","sign","floor","ceil","round","roundEven","trunc","fract","mod","modf","min","max","clamp","mix","step","smoothstep","length","distance","dot","cross ","determinant","inverse","normalize","faceforward","reflect","refract","matrixCompMult","outerProduct","transpose","lessThan ","lessThanEqual","greaterThan","greaterThanEqual","equal","notEqual","any","all","not","packUnorm2x16","unpackUnorm2x16","packSnorm2x16","unpackSnorm2x16","packHalf2x16","unpackHalf2x16","dFdx","dFdy","fwidth","textureSize","texture","textureProj","textureLod","textureGrad","texelFetch","texelFetchOffset","textureProjLod","textureLodOffset","textureGradOffset","textureProjLodOffset","textureProjGrad","intBitsToFloat","uintBitsToFloat","floatBitsToInt","floatBitsToUint","isnan","isinf","vec2","vec3","vec4","ivec2","ivec3","ivec4","uvec2","uvec3","uvec4","bvec2","bvec3","bvec4","mat2","mat3","mat2x2","mat2x3","mat2x4","mat3x2","mat3x3","mat3x4","mat4x2","mat4x3","mat4x4","mat4","float","int","uint","void","bool"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>{t.languages.register({id:"glsl"}),t.languages.setMonarchTokensProvider("glsl",p),t.languages.setLanguageConfiguration("glsl",m),"function"==typeof a.onMount&&a.onMount(e,t)}}))}},4896:(a,e,t)=>{"use strict";t.d(e,{m:()=>h});var s=t(7294),n=t(9477),m=t(5452),p=t(4866),r=t(5162),l=t(3764),i=t(5034),o=t(412),N=t(1410),c=t.n(N);function h(a){let{geometry:e,vertexShader:t,fragmentShader:N,showEditor:h=null}=a;const k=(0,s.useRef)(),g=(0,s.useRef)(),u=(0,s.useRef)(),d=(0,s.useRef)();function v(a,e,t){const s=[...e],m=[...e.slice(2)],p=[...t],r=[...t.slice(1)],l=[];let i=0;for(let c=0;c{const a=(1+Math.sqrt(5))/2,s=k.current.clientWidth,p=s*(.5/a),r=6*a,l=r*(.5/a),i=new n.iKG(r/-2,r/2,l/2,l/-2,-1e3,1e3);i.position.z=5;const h=new n.CP7({preserveDrawingBuffer:!0,powerPreference:"high-performance",antialias:!0,alpha:!0,premultipliedAlpha:!1});function f(){const e=k.current.clientWidth,t=.5*e/a;h.setSize(e,t)}h.setClearColor(new n.Ilk(1,1,1),0),h.setSize(s,p),d.current=h,window.addEventListener("resize",f),k.current.appendChild(h.domElement);const x=new n.xsS,w=new m.o(i,h.domElement);w.enableRotate=!1,w.enableDamping=!1,w.screenSpacePanning=!0,w.addEventListener("change",(()=>{h.render(x,i)})),g.current=()=>h.render(x,i),window.addEventListener("TextureLoaded",g.current);let y=new n.u9r;if("string"==typeof e){const a=[0,1,2,2,3,0];y.setIndex(a);const t=new Function(e),[s,n]=t();v(y,s,n)}else{if(!(e instanceof n.u9r))return void console.error("Unrecognized geometry input: "+typeof e);y=e}let b=new n.xEZ;o.Z.canUseDOM&&(b=(new n.dpR).load(`/${c().projectName}/img/stamp86.png`,(a=>{window.dispatchEvent(new CustomEvent("TextureLoaded"))}),void 0,void 0));const R={footprint:{value:b}},z=new n.FIo({vertexShader:t,fragmentShader:N,side:n.ehD,transparent:!0,glslVersion:n.LSk,uniforms:R});return u.current=new n.SPe(y,z,y.getAttribute("position0").count-1),u.current.frustumCulled=!1,x.add(u.current),g.current(),()=>{h.dispose(),window.removeEventListener("resize",f),window.removeEventListener("TextureLoaded",g.current)}}),[]);const x=(0,s.useCallback)(((a,e)=>{let t=[],s=[];try{const e=new Function(a);[t,s]=e()}catch(m){return void console.log(m.toString())}function n(a){if(Array.isArray(a)){for(let e=0;e{f(a,"")}})),R&&s.createElement(r.Z,{value:"fragment.glsl"},s.createElement(i.r,{height:w,defaultValue:N,onChange:a=>{f("",a)}})))),s.createElement("div",{ref:k,style:{width:"100%"},onMouseDown:a=>{a.preventDefault(),2==a.button&&console.log(d.current.domElement.toDataURL())}}))}},9279:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s="// Generate sinewave geometry \nconst maxRadius = 1/3;\nconst segmentCount = 32;\n\nconst position = [];\nconst radius = [];\n\nconst gr = (1 + Math.sqrt(5)) / 2; // golden ratio\nconst pi = Math.PI;\n\nfor(let i = 0; i <= segmentCount; ++i){\n let a = i / segmentCount\n let x = -pi + (2 * pi * a);\n let y = Math.sin(x) / gr;\n let r = Math.cos(x / 2.0) * maxRadius;\n\n position.push(x, y);\n radius.push(r);\n}\n\nreturn [position, radius];\n"},9086:(a,e,t)=>{"use strict";t.r(e),t.d(e,{assets:()=>d,contentTitle:()=>g,default:()=>w,frontMatter:()=>k,metadata:()=>u,toc:()=>v});var s=t(7462),n=t(7294),m=t(3905),p=t(4896),r=t(9279);const l="precision mediump float;\nprecision mediump int;\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\n\n// Every lines of code are the same, except for the length.\n// `length` is the distance from the current vertex to the very first vertex of the polyline\n// (the result of prefix sum on edge length).\nin float length0;\nin float length1;\nin float radius0;\nin float radius1;\nin vec2 position0;\nin vec2 position1;\n\nout vec2 p;\nflat out vec2 p0;\nflat out vec2 p1;\nflat out float r0;\nflat out float r1;\nflat out float l0;\nflat out float l1;\n\nvoid main(){\n p0 = position0;\n p1 = position1;\n r0 = radius0;\n r1 = radius1;\n // Pass the length values to fragment shader.\n l0 = length0;\n l1 = length1;\n\n float cosTheta = (r0 - r1)/distance(p0, p1);\n if(abs(cosTheta) >= 1.0) return;\n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n vec2 offsetSign = vec2[](\n vec2(-1.0,-1.0),\n vec2(-1.0, 1.0),\n vec2( 1.0, 1.0),\n vec2( 1.0,-1.0)\n )[gl_VertexID];\n vec2 position = vec2[](position0, position0, position1, position1)[gl_VertexID];\n float radius = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n float tanHalfTheta = sqrt((1.0+cosTheta) / (1.0-cosTheta));\n float cotHalfTheta = 1.0 / tanHalfTheta;\n float normalTanValue = vec4(tanHalfTheta, tanHalfTheta, cotHalfTheta, cotHalfTheta)[gl_VertexID];\n if(normalTanValue > 10.0 || normalTanValue < 0.1) return;\n\n vec2 trapzoidVertexPosition = position +\n offsetSign.x * radius * tangent +\n offsetSign.y * radius * normal * normalTanValue;\n p = trapzoidVertexPosition;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 0.0, 1.0);\n}",i='precision mediump float;\nprecision mediump int;\n\nout vec4 outColor;\n\nconst float interval = 0.2;\nuniform highp sampler2D footprint; // The stamp texture, linear filter\n\nin vec2 p;\n\nflat in vec2 p0;\nflat in vec2 p1;\nflat in float r0;\nflat in float r1;\nflat in float l0;\nflat in float l1;\n\nvoid main() {\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n float len = distance(p1, p0);\n\n vec2 pLocal = vec2(dot(p-p0, tangent), dot(p-p0, normal));\n float d0 = distance(p, p0);\n float d1 = distance(p, p1);\n float d0cos = pLocal.x / d0;\n float d1cos = (pLocal.x - len) / d1;\n float cosTheta = (r0 - r1)/len;\n\n if(d0cos < cosTheta && d0 > r0) discard;\n if(d1cos > cosTheta && d1 > r1) discard;\n // -------------------------------------------\n // The quadratic equation\n float a, b, c, delta;\n a = 1.0 - pow(cosTheta, 2.0);\n b = 2.0 * (r0 * cosTheta - pLocal.x);\n c = pow(pLocal.x, 2.0) + pow(pLocal.y, 2.0) - pow(r0, 2.0);\n delta = pow(b, 2.0) - 4.0*a*c;\n if(delta <= 0.0) discard;\n\n float tempMathBlock = b + sign(b) * sqrt(delta);\n float x1 = -2.0 * c / tempMathBlock;\n float x2 = -tempMathBlock / (2.0*a);\n vec2 temp = vec2(min(x1, x2), max(x1, x2));\n x1 = temp.x;\n x2 = temp.y;\n\n // With the distance to the polyline\'s first vertex, we can compute a "stamp index" value.\n float index0 = l0/interval; // The stamp index of vertex0.\n float startIndex, endIndex;\n startIndex = x1 < 0.0 ? ceil(index0):ceil(index0 + x1/interval);\n float index1 = l1/interval;\n float backIndex = x2/interval + index0;\n endIndex = index1 < backIndex ? index1 : backIndex;\n if(startIndex > endIndex) discard;\n\n // The main loop to sample and blend color from the footprint.\n int MAX_i = 128; float currIndex = startIndex;\n vec4 currColor = vec4(0.0);\n for(int i = 0; i < MAX_i; i++){\n float currStampLocalX = interval * (currIndex - index0);\n float currStampRadius = r0 - cosTheta * currStampLocalX;\n vec2 pToCurrStamp = pLocal - vec2(currStampLocalX, 0.0);\n vec2 textureCoordinate = (pToCurrStamp/currStampRadius + 1.0)/2.0;\n vec4 sampledColor = texture(footprint, textureCoordinate);\n\n // The alpha blending function\n vec4 color;\n color.a = sampledColor.a + currColor.a * (1.0 - sampledColor.a);\n color.rgb = (sampledColor.rgb * sampledColor.a + currColor.rgb * currColor.a * (1.0 - sampledColor.a))/color.a;\n\n currColor = color;\n currIndex += 1.0;\n if(currIndex > endIndex) break;\n }\n outColor = currColor;\n return;\n}\n';function o(a){let{showEditor:e=[!1,!1,!1]}=a;return n.createElement(p.m,{geometry:r.Z,vertexShader:l,fragmentShader:i,showEditor:e})}const N="precision mediump float;\nprecision mediump int;\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\n\n// Every lines of code are the same, except for the length.\n// `length` is the distance from the current vertex to the very first vertex of the polyline\n// (the result of prefix sum on edge length).\nin float length0;\nin float length1;\nin float radius0;\nin float radius1;\nin vec2 position0;\nin vec2 position1;\n\nout vec2 p;\nout float rp; // The interpolated radius value\nflat out vec2 p0;\nflat out vec2 p1;\nflat out float l0;\nflat out float l1;\n\n\nvoid main(){\n p0 = position0;\n p1 = position1;\n l0 = length0;\n l1 = length1;\n // Put r0 into left two vertices, r1 into right two vertices.\n rp = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n vec2 offsetSign = vec2[](\n vec2(-1.0,-1.0),\n vec2(-1.0, 1.0),\n vec2( 1.0, 1.0),\n vec2( 1.0,-1.0)\n )[gl_VertexID];\n vec2 position = vec2[](position0, position0, position1, position1)[gl_VertexID];\n float radius = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n // Place four vertices as the figure shows.\n // We don't have to calculate trigonometric values of theta, so many lines of code are saved.\n vec2 trapzoidVertexPosition = position +\n offsetSign.x * radius * tangent +\n offsetSign.y * radius * normal;\n p = trapzoidVertexPosition;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 0.0, 1.0);\n}",c="precision mediump float;\nprecision mediump int;\n\nout vec4 outColor;\n\nconst float interval = 0.2;\nuniform mediump sampler2D footprint;\n\nin vec2 p;\nin float rp; // The interpolated radius value\n\nflat in vec2 p0;\nflat in vec2 p1;\nflat in float l0;\nflat in float l1;\n\nvoid main() {\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n float len = distance(p1, p0);\n vec2 pLocal = vec2(dot(p-p0, tangent), dot(p-p0, normal));\n\n // Each stamp is a square, so we don't have to discard the corners.\n// float d0 = distance(p, p0);\n// float d1 = distance(p, p1);\n// if(pLocal.x < 0.0 && d0 > rp) discard;\n// if(pLocal.x > 0.0 && d1 > rp) discard;\n\n // Obviously, we are rendering a stroke with variable width, but we just assume it's a uni-width stroke with radius value `rp`.\n // The two furthest points, x1 and x2, can touch the current pixel is much easier to compute.\n // We don't need to solve the quadratic equation for each pixel, which is the main source of the performance benefit.\n float x1 = pLocal.x - rp;\n float x2 = pLocal.x + rp;\n\n float index0 = l0/interval;\n float startIndex, endIndex;\n if (x1 <= 0.0){\n startIndex = ceil(index0);\n }\n else{\n startIndex = ceil(index0 + x1/interval);\n }\n float index1 = l1/interval;\n float backIndex = x2/interval + index0;\n endIndex = index1 < backIndex ? index1 : backIndex;\n if(startIndex > endIndex) discard;\n\n int MAX_i = 128; float currIndex = startIndex;\n vec4 currColor = vec4(0.0);\n for(int i = 0; i < MAX_i; i++){\n float currStampLocalX = interval * (currIndex - index0);\n vec2 pToCurrStamp = pLocal - vec2(currStampLocalX, 0.0);\n vec2 textureCoordinate = (pToCurrStamp/rp + 1.0)/2.0;\n vec4 sampledColor = texture(footprint, textureCoordinate);\n // The alpha blending function\n vec4 color;\n color.a = sampledColor.a + currColor.a * (1.0 - sampledColor.a);\n color.rgb = (sampledColor.rgb * sampledColor.a + currColor.rgb * currColor.a * (1.0 - sampledColor.a))/color.a;\n\n currColor = color;\n currIndex += 1.0;\n if(currIndex > endIndex) break;\n }\n outColor = currColor;\n return;\n}";function h(a){let{showEditor:e=[!1,!1,!1]}=a;return n.createElement(p.m,{geometry:r.Z,vertexShader:N,fragmentShader:c,showEditor:e})}const k={sidebar_position:2},g=void 0,u={unversionedId:"Basics/Stamp/Stamp",id:"Basics/Stamp/Stamp",title:"Stamp",description:"Intro",source:"@site/docs/Basics/Stamp/Stamp.mdx",sourceDirName:"Basics/Stamp",slug:"/Basics/Stamp/",permalink:"/brush-rendering-tutorial/Basics/Stamp/",draft:!1,editUrl:"https://github.com/ShenCiao/brush-rendering-tutorial/tree/main/docs/Basics/Stamp/Stamp.mdx",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Vanilla",permalink:"/brush-rendering-tutorial/Basics/Vanilla/"},next:{title:"Tessellation",permalink:"/brush-rendering-tutorial/Tessellation/"}},d={},v=[{value:"Intro",id:"intro",level:2},{value:"Locate stamps",id:"locate-stamps",level:2},{value:"Loop and sample stamps",id:"loop-and-sample-stamps",level:2},{value:"Square footprint",id:"square-footprint",level:2}],f={toc:v},x="wrapper";function w(a){let{components:e,...n}=a;return(0,m.kt)(x,(0,s.Z)({},f,n,{components:e,mdxType:"MDXLayout"}),(0,m.kt)("h2",{id:"intro"},"Intro"),(0,m.kt)("p",null,"The basic stamp stroke rendering is pretty intuitive.\nWe are given a texture called stamp texture or footprint.\nWhile a user paints on a canvas, we render the texture onto the canvas equidistantly along the drawing trace.\nWhen the textures are close enough, they seem continuous and form a stroke."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"tostroke",src:t(775).Z,width:"750",height:"234"})),(0,m.kt)("p",null,"Artists love to create stamp brushes since their expressiveness and intuition.\nMore than 90% of brushes in popular paint software are stamp brushes.\nResearchers and developers have developed dozens of extra parameters\nthat can apply various aspects of stylization on a stroke.\nHow to replicate these parameters with GPU acceleration is under-researched.\nTherefore, we will focus on the most critical technique, how to place the footprints in a shader program."),(0,m.kt)("p",null,"A naive solution is to place a footprint at each vertex.\n",(0,m.kt)("img",{alt:"naive",src:t(899).Z,width:"1276",height:"897"}),"\n",(0,m.kt)("em",{parentName:"p"},"Place a dot texture at each vertex.")),(0,m.kt)("p",null,"But it's not good enough for the most usages.\nThe rendering result of strokes depends on polylines' vertex density.\nAfter subdivision, simplification, or deformation of the polylines, the strokes get denser or sparser appearance, which ruins the rendering result.\nWe must develop methods that rendering is independent of vertex density, just like rendering 3D meshes.\nTherefore, I will introduce how to place footprints equidistantly along a polyline."),(0,m.kt)("h2",{id:"locate-stamps"},"Locate stamps"),(0,m.kt)("p",null,"When rendering with CPU, calculating where to place stamp (stamp positions) on a polyline is pretty straightforward.\nStart from the very first vertex of the polyline,\nmove along the polyline with the stamp interval to the next stamp position,\nloop until reach the end of polyline.\nIt is trivial for experienced developers to implement the process above."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"pos",src:t(2054).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"GPU implementation is more challenging, but still relatively straightforward.\nWe use the same approach as rendering a vanilla stroke:\nPlacing the four new vertices for each edge in the vertex shader (or the geometry shader).\nThe difference only comes to the fragment shader."),(0,m.kt)("p",null,"We will calculate the stamp positions in the fragment shader.\nAt each polyline vertex, we compute its distance to the first vertex along the polyline.\nThe distances represent the cumulative length of the edges, and we calculated them with Prefix Sum algorithm."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"len",src:t(2921).Z,width:"2560",height:"1080"})),(0,m.kt)("details",null,(0,m.kt)("summary",null," Parallel prefix sum "),(0,m.kt)("p",null," There are parallel prefix sum algorithms can be implemented with compute shaders,\n",(0,m.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Prefix_sum#Parallel_algorithms"},"wiki"),".\nBy using one of them, the whole rendering process is GPU-accelerated."),(0,m.kt)("p",null," The best tutorial about parallel prefix sum I've ever found is this free course\n",(0,m.kt)("a",{parentName:"p",href:"https://www.udacity.com/course/high-performance-computing--ud281"},"High Performance Computing")," by Prof. Rich Vuduc.\nThe course introduces prefix sum in Lesson 6.")),(0,m.kt)("p",null,"Since we place stamps equidistantly, we can calculate these positions in all pixels involved in each edge.\nThen a pixel can loop through the positions, sample the stamp textures and determine its color.\nWe compute the texture coordinate with the pixel's distance to the stamp positions and radii."),(0,m.kt)("h2",{id:"loop-and-sample-stamps"},"Loop and sample stamps"),(0,m.kt)("p",null,"A pixel can loop through all stamps on the edge invokes it.\nBut the edge can be very long and has many unnecessary stamps for a pixel to loop through.\nWe want to constrain the loop within only a segment of the edge, within which stamps can cover the current pixel.\nGiven the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),", we can get this segment by solving the geometry shown in the figure below."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"range",src:t(4962).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"I label the segment can cover the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p")))))," with the thicker black line.\nThe two dashed circles intersect at ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),".\nI label their centers with vertical line ticks, which are the start and end points of the segment.\nThe centers are the farthest points where a stamp placed can cover the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),".\nAny stamps outside the segment have no chance to affect the pixel's color."),(0,m.kt)("p",null,"In order to get start and end points' positions, we have a geometry puzzle to solve:\n",(0,m.kt)("img",{alt:"func",src:t(8372).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"As the figure shows, we set up the same local coordinate as vanilla stroke in the shader program.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"p"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," as the origin and X and Y axes align to the tangent and normal direction,\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",separator:"true"},","),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"y"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"(x_p, y_p)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mpunct"},","),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," as the pixel's local coordinate.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"x")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"x")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x")))))," is the furthest point's x value.\nBy calculating it, we know the position of farthest point.\nThe distance from the pixel and the furthest point ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," is the radius of the furthest point, whose value is determined by x.\nTo solve the unknown ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"x")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"x")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"))))),", we can set an equation from the dashed right triangle ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mo",{parentName:"msup",stretchy:"false"},")"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mo",{parentName:"msup",stretchy:"false"},")"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"y"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)^2 = (x_p - x)^2 + y_p^2")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0641em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0641em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))))))),"."),(0,m.kt)("p",null,"It's not hard to derive ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mn",{parentName:"mrow"},"1"),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x) = r_1 * x/l + r_0 * (1-x/l)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6153em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6153em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},"1"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," from the figure below.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"l")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"l")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l")))))," is the length of the current edge.\n",(0,m.kt)("img",{alt:"xradius",src:t(7637).Z,width:"2560",height:"1080"}),"\nBy replacing ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"))))),", we get a quadratic equation ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"a"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msup"},"x"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("mi",{parentName:"mrow"},"b"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("mi",{parentName:"mrow"},"c"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mn",{parentName:"mrow"},"0")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"ax^2 + bx + c = 0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.8974em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"a"),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7778em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"b"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"c"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6444em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"0"))))),":"),(0,m.kt)("p",null,(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"a"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mn",{parentName:"mrow"},"1"),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mrow",{parentName:"msup"},(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061")),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"a = 1 - \\cos^2\\theta")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"a"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7278em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"1"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.8141em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"))))),";\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"b"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mn",{parentName:"mrow"},"2"),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"b = - 2 * (r_0\\cos\\theta + x_p)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"b"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7278em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mord"},"2"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"))))),";\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"c"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"x"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"y"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"r"),(0,m.kt)("mn",{parentName:"msubsup"},"0"),(0,m.kt)("mn",{parentName:"msubsup"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"c = x_p^2 + y_p^2 - r_0^2")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"c"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0622em",verticalAlign:"-0.2481em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.4519em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2481em"}},(0,m.kt)("span",{parentName:"span"}))))))))))),(0,m.kt)("p",null,"Remind that ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"\\cos\\theta = (r_0 - r_1)/l")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"))))),".\nApplying the formula for solving quadratic equations,\ntwo roots of the equation are the X value of min and max points of the segment.\nTherefore, we know the range in the fragment shader and pixels only loop through stamps that can cover it."),(0,m.kt)(o,{showEditor:[!1,!0,!0],mdxType:"StampRound"}),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"texture",src:t(9602).Z,width:"512",height:"512"})),(0,m.kt)("p",null,"You can use RGBA values sampled from footprints directly, like what I did.\nBut for the most common scenario,\nwe set RGB values with users' brush setting and sample alpha values from a monochrome texture,\nwhose pixel's gray scale determines the opacity.\nThen apply random rotation and noise on each footprint to further stylize the stroke.\nThe stamp index values ",(0,m.kt)("inlineCode",{parentName:"p"},"currIndex")," are helpful to generate consistent random numbers as seed values."),(0,m.kt)("h2",{id:"square-footprint"},"Square footprint"),(0,m.kt)("p",null,"I introduce the brush rendering with the assumption that footprints are constrained within a dot area,\nwhich is the most common case in practice.\nBut it's not for all.\nArea outside the dot area plays a critical role sometimes.\nIf it is necessary to render square footprints, I guess experienced graphics developers can instantly get some ideas on\nnew approaches to placing the vertices and determining stamp positions.\nBut they may hurt the performance and have complex logic to implement."),(0,m.kt)("p",null,'I will introduce a very tricky approach to rendering the square footprint.\nMeanwhile, improve the rendering performance and reduce the complexity of the shader code.\nAll we have to sacrifice is the "correctness" of geometry, which is trivial for artists.'),(0,m.kt)("p",null,"We place vertices a little bit differently from before.\nInstead of offset vertices by ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mi",{parentName:"mrow"},"tan"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mfrac",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"mfrac"},"\u03b8"),(0,m.kt)("mn",{parentName:"mfrac"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0\\tan\\frac{\\theta}{2}")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.2251em",verticalAlign:"-0.345em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"tan"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mopen nulldelimiter"}),(0,m.kt)("span",{parentName:"span",className:"mfrac"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8801em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.655em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.23em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"frac-line",style:{borderBottomWidth:"0.04em"}})),(0,m.kt)("span",{parentName:"span",style:{top:"-3.394em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight",style:{marginRight:"0.02778em"}},"\u03b8"))))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.345em"}},(0,m.kt)("span",{parentName:"span"}))))),(0,m.kt)("span",{parentName:"span",className:"mclose nulldelimiter"}))))))," and ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mi",{parentName:"mrow"},"cot"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mfrac",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"mfrac"},"\u03b8"),(0,m.kt)("mn",{parentName:"mfrac"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1\\cot\\frac{\\theta}{2}")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.2251em",verticalAlign:"-0.345em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cot"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mopen nulldelimiter"}),(0,m.kt)("span",{parentName:"span",className:"mfrac"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8801em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.655em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.23em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"frac-line",style:{borderBottomWidth:"0.04em"}})),(0,m.kt)("span",{parentName:"span",style:{top:"-3.394em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight",style:{marginRight:"0.02778em"}},"\u03b8"))))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.345em"}},(0,m.kt)("span",{parentName:"span"}))))),(0,m.kt)("span",{parentName:"span",className:"mclose nulldelimiter"}))))))," along the normal direction,\nwe just offset ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," and ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," value, as the figure below shows."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"square",src:t(1889).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"In the vertex shader or geometry shader, we put the radius value ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," to the left two vertices,\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," to the right two vertices, and let fragment shader interpolate the values for us.\nThen we get an interpolated radius value ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," in the fragment shader.\nYou can prove this ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," is the stroke radius value across the current pixel as the figure shows.\nWe assume the whole stroke is uni-radius and program the shader with ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))))))),"."),(0,m.kt)(h,{showEditor:[!1,!0,!0],mdxType:"StampSquare"}),(0,m.kt)("p",null,"I basically cannot perceive the deformation caused by the trick at the first glance."))}w.isMDXComponent=!0},899:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/dotted-monkey-cf1ca1e77f00864b77d2fead263e3f4c.png"},8372:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-function-464d09193dd34a0055f5138889207f02.png"},2921:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-length-ea5d8a9b4e5cdf3e5441b2d53bbe14eb.png"},2054:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-position-1645edccb9edc05441593af193fc4ed0.png"},4962:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-range-f8e087a122c236bb0d125adf03d39a50.png"},1889:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-square-a31970ed55df23f105d4f72c416aba86.png"},775:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-to-stroke-082a5ddd80c45086b810ed8b9ebcea79.gif"},7637:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-xradius-81321c92495722f37655ad34de62103b.png"},9602:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp86-ec89b059698b43632871fa23c95706eb.png"}}]); \ No newline at end of file diff --git a/assets/js/e7f20846.5a3c2c8d.js b/assets/js/e7f20846.5a3c2c8d.js new file mode 100644 index 0000000..25796bc --- /dev/null +++ b/assets/js/e7f20846.5a3c2c8d.js @@ -0,0 +1 @@ +(self.webpackChunkbrush_stroke_tutorial=self.webpackChunkbrush_stroke_tutorial||[]).push([[342],{1410:(a,e,t)=>{const s=t(7694),n=t(3618),m={title:"Brush Rendering Tutorial",tagline:"Learn brush stroke rendering.",favicon:"img/favicon.png",url:"https://shenciao.github.io",baseUrl:"/brush-rendering-tutorial/",organizationName:"ShenCiao",projectName:"brush-rendering-tutorial",onBrokenLinks:"throw",onBrokenMarkdownLinks:"warn",i18n:{defaultLocale:"en",locales:["en"]},presets:[["classic",{docs:{remarkPlugins:[t(3545)],rehypePlugins:[t(1167)],routeBasePath:"/",sidebarPath:6679,editUrl:"https://github.com/ShenCiao/brush-rendering-tutorial/tree/main"},blog:!1,theme:{customCss:2295}}]],themeConfig:{colorMode:{disableSwitch:!0},image:"img/vanilla-stroke.png",navbar:{title:"Brush Rendering Tutorial",logo:{alt:"logo",src:"img/vanilla-stroke.png"},items:[{type:"docSidebar",sidebarId:"tutorialSidebar",position:"right",label:"Tutorial"},{href:"https://github.com/ShenCiao/brush-stroke-tutorial",label:"GitHub",position:"right"}]},footer:{style:"light",copyright:`Copyright \xa9 ${(new Date).getFullYear()} Brush Rendering Tutorial by Shen Ciao, under CC BY-SA 4.0 License`},prism:{theme:s,darkTheme:n},docs:{sidebar:{hideable:!0}},stylesheets:[{href:"https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css",type:"text/css",integrity:"sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM",crossorigin:"anonymous"}]},plugins:["raw-loaders"],trailingSlash:!0};a.exports=m},6679:a=>{a.exports={tutorialSidebar:[{type:"autogenerated",dirName:"."}]}},5034:(a,e,t)=>{"use strict";t.d(e,{r:()=>l});var s=t(7462),n=t(7294);const m={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"[",close:"]"},{open:"{",close:"}"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},p={tokenPostfix:".glsl",defaultToken:"invalid",keywords:["const","uniform","break","continue","do","for","while","if","else","switch","case","in","out","inout","true","false","invariant","discard","return","sampler2D","samplerCube","sampler3D","struct","radians","degrees","sin","cos","tan","asin","acos","atan","pow","sinh","cosh","tanh","asinh","acosh","atanh","exp","log","exp2","log2","sqrt","inversesqrt","abs","sign","floor","ceil","round","roundEven","trunc","fract","mod","modf","min","max","clamp","mix","step","smoothstep","length","distance","dot","cross ","determinant","inverse","normalize","faceforward","reflect","refract","matrixCompMult","outerProduct","transpose","lessThan ","lessThanEqual","greaterThan","greaterThanEqual","equal","notEqual","any","all","not","packUnorm2x16","unpackUnorm2x16","packSnorm2x16","unpackSnorm2x16","packHalf2x16","unpackHalf2x16","dFdx","dFdy","fwidth","textureSize","texture","textureProj","textureLod","textureGrad","texelFetch","texelFetchOffset","textureProjLod","textureLodOffset","textureGradOffset","textureProjLodOffset","textureProjGrad","intBitsToFloat","uintBitsToFloat","floatBitsToInt","floatBitsToUint","isnan","isinf","vec2","vec3","vec4","ivec2","ivec3","ivec4","uvec2","uvec3","uvec4","bvec2","bvec3","bvec4","mat2","mat3","mat2x2","mat2x3","mat2x4","mat3x2","mat3x3","mat3x4","mat4x2","mat4x3","mat4x4","mat4","float","int","uint","void","bool"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>{t.languages.register({id:"glsl"}),t.languages.setMonarchTokensProvider("glsl",p),t.languages.setLanguageConfiguration("glsl",m),"function"==typeof a.onMount&&a.onMount(e,t)}}))}},4896:(a,e,t)=>{"use strict";t.d(e,{m:()=>h});var s=t(7294),n=t(9477),m=t(5452),p=t(4866),r=t(5162),l=t(3764),i=t(5034),o=t(412),N=t(1410),c=t.n(N);function h(a){let{geometry:e,vertexShader:t,fragmentShader:N,showEditor:h=null}=a;const k=(0,s.useRef)(),g=(0,s.useRef)(),u=(0,s.useRef)(),d=(0,s.useRef)();function v(a,e,t){const s=[...e],m=[...e.slice(2)],p=[...t],r=[...t.slice(1)],l=[];let i=0;for(let c=0;c{const a=(1+Math.sqrt(5))/2,s=k.current.clientWidth,p=s*(.5/a),r=6*a,l=r*(.5/a),i=new n.iKG(r/-2,r/2,l/2,l/-2,-1e3,1e3);i.position.z=5;const h=new n.CP7({preserveDrawingBuffer:!0,powerPreference:"high-performance",antialias:!0,alpha:!0,premultipliedAlpha:!1});function f(){const e=k.current.clientWidth,t=.5*e/a;h.setSize(e,t)}h.setClearColor(new n.Ilk(1,1,1),0),h.setSize(s,p),d.current=h,window.addEventListener("resize",f),k.current.appendChild(h.domElement);const x=new n.xsS,w=new m.o(i,h.domElement);w.enableRotate=!1,w.enableDamping=!1,w.screenSpacePanning=!0,w.addEventListener("change",(()=>{h.render(x,i)})),g.current=()=>h.render(x,i),window.addEventListener("TextureLoaded",g.current);let y=new n.u9r;if("string"==typeof e){const a=[0,1,2,2,3,0];y.setIndex(a);const t=new Function(e),[s,n]=t();v(y,s,n)}else{if(!(e instanceof n.u9r))return void console.error("Unrecognized geometry input: "+typeof e);y=e}let b=new n.xEZ;o.Z.canUseDOM&&(b=(new n.dpR).load(`/${c().projectName}/img/stamp86.png`,(a=>{window.dispatchEvent(new CustomEvent("TextureLoaded"))}),void 0,void 0));const R={footprint:{value:b}},z=new n.FIo({vertexShader:t,fragmentShader:N,side:n.ehD,transparent:!0,glslVersion:n.LSk,uniforms:R});return u.current=new n.SPe(y,z,y.getAttribute("position0").count-1),u.current.frustumCulled=!1,x.add(u.current),g.current(),()=>{h.dispose(),window.removeEventListener("resize",f),window.removeEventListener("TextureLoaded",g.current)}}),[]);const x=(0,s.useCallback)(((a,e)=>{let t=[],s=[];try{const e=new Function(a);[t,s]=e()}catch(m){return void console.log(m.toString())}function n(a){if(Array.isArray(a)){for(let e=0;e{f(a,"")}})),R&&s.createElement(r.Z,{value:"fragment.glsl"},s.createElement(i.r,{height:w,defaultValue:N,onChange:a=>{f("",a)}})))),s.createElement("div",{ref:k,style:{width:"100%"},onMouseDown:a=>{a.preventDefault(),2==a.button&&console.log(d.current.domElement.toDataURL())}}))}},9279:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s="// Generate sinewave geometry \nconst maxRadius = 1/3;\nconst segmentCount = 32;\n\nconst position = [];\nconst radius = [];\n\nconst gr = (1 + Math.sqrt(5)) / 2; // golden ratio\nconst pi = Math.PI;\n\nfor(let i = 0; i <= segmentCount; ++i){\n let a = i / segmentCount\n let x = -pi + (2 * pi * a);\n let y = Math.sin(x) / gr;\n let r = Math.cos(x / 2.0) * maxRadius;\n\n position.push(x, y);\n radius.push(r);\n}\n\nreturn [position, radius];\n"},9086:(a,e,t)=>{"use strict";t.r(e),t.d(e,{assets:()=>d,contentTitle:()=>g,default:()=>w,frontMatter:()=>k,metadata:()=>u,toc:()=>v});var s=t(7462),n=t(7294),m=t(3905),p=t(4896),r=t(9279);const l="precision mediump float;\nprecision mediump int;\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\n\n// Every lines of code are the same, except for the length.\n// `length` is the distance from the current vertex to the very first vertex of the polyline\n// (the result of prefix sum on edge length).\nin float length0;\nin float length1;\nin float radius0;\nin float radius1;\nin vec2 position0;\nin vec2 position1;\n\nout vec2 p;\nflat out vec2 p0;\nflat out vec2 p1;\nflat out float r0;\nflat out float r1;\nflat out float l0;\nflat out float l1;\n\nvoid main(){\n p0 = position0;\n p1 = position1;\n r0 = radius0;\n r1 = radius1;\n // Pass the length values to fragment shader.\n l0 = length0;\n l1 = length1;\n\n float cosTheta = (r0 - r1)/distance(p0, p1);\n if(abs(cosTheta) >= 1.0) return;\n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n vec2 offsetSign = vec2[](\n vec2(-1.0,-1.0),\n vec2(-1.0, 1.0),\n vec2( 1.0, 1.0),\n vec2( 1.0,-1.0)\n )[gl_VertexID];\n vec2 position = vec2[](position0, position0, position1, position1)[gl_VertexID];\n float radius = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n float tanHalfTheta = sqrt((1.0+cosTheta) / (1.0-cosTheta));\n float cotHalfTheta = 1.0 / tanHalfTheta;\n float normalTanValue = vec4(tanHalfTheta, tanHalfTheta, cotHalfTheta, cotHalfTheta)[gl_VertexID];\n if(normalTanValue > 10.0 || normalTanValue < 0.1) return;\n\n vec2 trapzoidVertexPosition = position +\n offsetSign.x * radius * tangent +\n offsetSign.y * radius * normal * normalTanValue;\n p = trapzoidVertexPosition;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 0.0, 1.0);\n}",i='precision mediump float;\nprecision mediump int;\n\nout vec4 outColor;\n\nconst float interval = 0.2;\nuniform highp sampler2D footprint; // The stamp texture, linear filter\n\nin vec2 p;\n\nflat in vec2 p0;\nflat in vec2 p1;\nflat in float r0;\nflat in float r1;\nflat in float l0;\nflat in float l1;\n\nvoid main() {\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n float len = distance(p1, p0);\n\n vec2 pLocal = vec2(dot(p-p0, tangent), dot(p-p0, normal));\n float d0 = distance(p, p0);\n float d1 = distance(p, p1);\n float d0cos = pLocal.x / d0;\n float d1cos = (pLocal.x - len) / d1;\n float cosTheta = (r0 - r1)/len;\n\n if(d0cos < cosTheta && d0 > r0) discard;\n if(d1cos > cosTheta && d1 > r1) discard;\n // -------------------------------------------\n // The quadratic equation\n float a, b, c, delta;\n a = 1.0 - pow(cosTheta, 2.0);\n b = 2.0 * (r0 * cosTheta - pLocal.x);\n c = pow(pLocal.x, 2.0) + pow(pLocal.y, 2.0) - pow(r0, 2.0);\n delta = pow(b, 2.0) - 4.0*a*c;\n if(delta <= 0.0) discard;\n\n float tempMathBlock = b + sign(b) * sqrt(delta);\n float x1 = -2.0 * c / tempMathBlock;\n float x2 = -tempMathBlock / (2.0*a);\n vec2 temp = vec2(min(x1, x2), max(x1, x2));\n x1 = temp.x;\n x2 = temp.y;\n\n // With the distance to the polyline\'s first vertex, we can compute a "stamp index" value.\n float index0 = l0/interval; // The stamp index of vertex0.\n float startIndex, endIndex;\n startIndex = x1 < 0.0 ? ceil(index0):ceil(index0 + x1/interval);\n float index1 = l1/interval;\n float backIndex = x2/interval + index0;\n endIndex = index1 < backIndex ? index1 : backIndex;\n if(startIndex > endIndex) discard;\n\n // The main loop to sample and blend color from the footprint.\n int MAX_i = 128; float currIndex = startIndex;\n vec4 currColor = vec4(0.0,0.0,0.0,1e-10);\n for(int i = 0; i < MAX_i; i++){\n float currStampLocalX = interval * (currIndex - index0);\n float currStampRadius = r0 - cosTheta * currStampLocalX;\n vec2 pToCurrStamp = pLocal - vec2(currStampLocalX, 0.0);\n vec2 textureCoordinate = (pToCurrStamp/currStampRadius + 1.0)/2.0;\n vec4 sampledColor = texture(footprint, textureCoordinate);\n\n // The alpha blending function\n vec4 color;\n color.a = sampledColor.a + currColor.a * (1.0 - sampledColor.a);\n color.rgb = (sampledColor.rgb * sampledColor.a + currColor.rgb * currColor.a * (1.0 - sampledColor.a))/color.a;\n\n currColor = color;\n currIndex += 1.0;\n if(currIndex > endIndex) break;\n }\n outColor = currColor;\n return;\n}\n';function o(a){let{showEditor:e=[!1,!1,!1]}=a;return n.createElement(p.m,{geometry:r.Z,vertexShader:l,fragmentShader:i,showEditor:e})}const N="precision mediump float;\nprecision mediump int;\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\n\n// Every lines of code are the same, except for the length.\n// `length` is the distance from the current vertex to the very first vertex of the polyline\n// (the result of prefix sum on edge length).\nin float length0;\nin float length1;\nin float radius0;\nin float radius1;\nin vec2 position0;\nin vec2 position1;\n\nout vec2 p;\nout float rp; // The interpolated radius value\nflat out vec2 p0;\nflat out vec2 p1;\nflat out float l0;\nflat out float l1;\n\n\nvoid main(){\n p0 = position0;\n p1 = position1;\n l0 = length0;\n l1 = length1;\n // Put r0 into left two vertices, r1 into right two vertices.\n rp = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n vec2 offsetSign = vec2[](\n vec2(-1.0,-1.0),\n vec2(-1.0, 1.0),\n vec2( 1.0, 1.0),\n vec2( 1.0,-1.0)\n )[gl_VertexID];\n vec2 position = vec2[](position0, position0, position1, position1)[gl_VertexID];\n float radius = vec4(radius0, radius0, radius1, radius1)[gl_VertexID];\n\n // Place four vertices as the figure shows.\n // We don't have to calculate trigonometric values of theta, so many lines of code are saved.\n vec2 trapzoidVertexPosition = position +\n offsetSign.x * radius * tangent +\n offsetSign.y * radius * normal;\n p = trapzoidVertexPosition;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 0.0, 1.0);\n}",c="precision mediump float;\nprecision mediump int;\n\nout vec4 outColor;\n\nconst float interval = 0.2;\nuniform mediump sampler2D footprint;\n\nin vec2 p;\nin float rp; // The interpolated radius value\n\nflat in vec2 p0;\nflat in vec2 p1;\nflat in float l0;\nflat in float l1;\n\nvoid main() {\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n float len = distance(p1, p0);\n vec2 pLocal = vec2(dot(p-p0, tangent), dot(p-p0, normal));\n\n // Each stamp is a square, so we don't have to discard the corners.\n// float d0 = distance(p, p0);\n// float d1 = distance(p, p1);\n// if(pLocal.x < 0.0 && d0 > rp) discard;\n// if(pLocal.x > 0.0 && d1 > rp) discard;\n\n // Obviously, we are rendering a stroke with variable width, but we just assume it's a uni-width stroke with radius value `rp`.\n // The two furthest points, x1 and x2, can touch the current pixel is much easier to compute.\n // We don't need to solve the quadratic equation for each pixel, which is the main source of the performance benefit.\n float x1 = pLocal.x - rp;\n float x2 = pLocal.x + rp;\n\n float index0 = l0/interval;\n float startIndex, endIndex;\n if (x1 <= 0.0){\n startIndex = ceil(index0);\n }\n else{\n startIndex = ceil(index0 + x1/interval);\n }\n float index1 = l1/interval;\n float backIndex = x2/interval + index0;\n endIndex = index1 < backIndex ? index1 : backIndex;\n if(startIndex > endIndex) discard;\n\n int MAX_i = 128; float currIndex = startIndex;\n vec4 currColor = vec4(0.0);\n for(int i = 0; i < MAX_i; i++){\n float currStampLocalX = interval * (currIndex - index0);\n vec2 pToCurrStamp = pLocal - vec2(currStampLocalX, 0.0);\n vec2 textureCoordinate = (pToCurrStamp/rp + 1.0)/2.0;\n vec4 sampledColor = texture(footprint, textureCoordinate);\n // The alpha blending function\n vec4 color;\n color.a = sampledColor.a + currColor.a * (1.0 - sampledColor.a);\n color.rgb = (sampledColor.rgb * sampledColor.a + currColor.rgb * currColor.a * (1.0 - sampledColor.a))/color.a;\n\n currColor = color;\n currIndex += 1.0;\n if(currIndex > endIndex) break;\n }\n outColor = currColor;\n return;\n}";function h(a){let{showEditor:e=[!1,!1,!1]}=a;return n.createElement(p.m,{geometry:r.Z,vertexShader:N,fragmentShader:c,showEditor:e})}const k={sidebar_position:2},g=void 0,u={unversionedId:"Basics/Stamp/Stamp",id:"Basics/Stamp/Stamp",title:"Stamp",description:"Intro",source:"@site/docs/Basics/Stamp/Stamp.mdx",sourceDirName:"Basics/Stamp",slug:"/Basics/Stamp/",permalink:"/brush-rendering-tutorial/Basics/Stamp/",draft:!1,editUrl:"https://github.com/ShenCiao/brush-rendering-tutorial/tree/main/docs/Basics/Stamp/Stamp.mdx",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Vanilla",permalink:"/brush-rendering-tutorial/Basics/Vanilla/"},next:{title:"Tessellation",permalink:"/brush-rendering-tutorial/Tessellation/"}},d={},v=[{value:"Intro",id:"intro",level:2},{value:"Locate stamps",id:"locate-stamps",level:2},{value:"Loop and sample stamps",id:"loop-and-sample-stamps",level:2},{value:"Square footprint",id:"square-footprint",level:2}],f={toc:v},x="wrapper";function w(a){let{components:e,...n}=a;return(0,m.kt)(x,(0,s.Z)({},f,n,{components:e,mdxType:"MDXLayout"}),(0,m.kt)("h2",{id:"intro"},"Intro"),(0,m.kt)("p",null,"The basic stamp stroke rendering is pretty intuitive.\nWe are given a texture called stamp texture or footprint.\nWhile a user paints on a canvas, we render the texture onto the canvas equidistantly along the drawing trace.\nWhen the textures are close enough, they seem continuous and form a stroke."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"tostroke",src:t(775).Z,width:"750",height:"234"})),(0,m.kt)("p",null,"Artists love to create stamp brushes since their expressiveness and intuition.\nMore than 90% of brushes in popular paint software are stamp brushes.\nResearchers and developers have developed dozens of extra parameters\nthat can apply various aspects of stylization on a stroke.\nHow to replicate these parameters with GPU acceleration is under-researched.\nTherefore, we will focus on the most critical technique, how to place the footprints in a shader program."),(0,m.kt)("p",null,"A naive solution is to place a footprint at each vertex.\n",(0,m.kt)("img",{alt:"naive",src:t(899).Z,width:"1276",height:"897"}),"\n",(0,m.kt)("em",{parentName:"p"},"Place a dot texture at each vertex.")),(0,m.kt)("p",null,"But it's not good enough for the most usages.\nThe rendering result of strokes depends on polylines' vertex density.\nAfter subdivision, simplification, or deformation of the polylines, the strokes get denser or sparser appearance, which ruins the rendering result.\nWe must develop methods that rendering is independent of vertex density, just like rendering 3D meshes.\nTherefore, I will introduce how to place footprints equidistantly along a polyline."),(0,m.kt)("h2",{id:"locate-stamps"},"Locate stamps"),(0,m.kt)("p",null,"When rendering with CPU, calculating where to place stamp (stamp positions) on a polyline is pretty straightforward.\nStart from the very first vertex of the polyline,\nmove along the polyline with the stamp interval to the next stamp position,\nloop until reach the end of polyline.\nIt is trivial for experienced developers to implement the process above."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"pos",src:t(2054).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"GPU implementation is more challenging, but still relatively straightforward.\nWe use the same approach as rendering a vanilla stroke:\nPlacing the four new vertices for each edge in the vertex shader (or the geometry shader).\nThe difference only comes to the fragment shader."),(0,m.kt)("p",null,"We will calculate the stamp positions in the fragment shader.\nAt each polyline vertex, we compute its distance to the first vertex along the polyline.\nThe distances represent the cumulative length of the edges, and we calculated them with Prefix Sum algorithm."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"len",src:t(2921).Z,width:"2560",height:"1080"})),(0,m.kt)("details",null,(0,m.kt)("summary",null," Parallel prefix sum "),(0,m.kt)("p",null," There are parallel prefix sum algorithms can be implemented with compute shaders,\n",(0,m.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Prefix_sum#Parallel_algorithms"},"wiki"),".\nBy using one of them, the whole rendering process is GPU-accelerated."),(0,m.kt)("p",null," The best tutorial about parallel prefix sum I've ever found is this free course\n",(0,m.kt)("a",{parentName:"p",href:"https://www.udacity.com/course/high-performance-computing--ud281"},"High Performance Computing")," by Prof. Rich Vuduc.\nThe course introduces prefix sum in Lesson 6.")),(0,m.kt)("p",null,"Since we place stamps equidistantly, we can calculate these positions in all pixels involved in each edge.\nThen a pixel can loop through the positions, sample the stamp textures and determine its color.\nWe compute the texture coordinate with the pixel's distance to the stamp positions and radii."),(0,m.kt)("h2",{id:"loop-and-sample-stamps"},"Loop and sample stamps"),(0,m.kt)("p",null,"A pixel can loop through all stamps on the edge invokes it.\nBut the edge can be very long and has many unnecessary stamps for a pixel to loop through.\nWe want to constrain the loop within only a segment of the edge, within which stamps can cover the current pixel.\nGiven the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),", we can get this segment by solving the geometry shown in the figure below."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"range",src:t(4962).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"I label the segment can cover the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p")))))," with the thicker black line.\nThe two dashed circles intersect at ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),".\nI label their centers with vertical line ticks, which are the start and end points of the segment.\nThe centers are the farthest points where a stamp placed can cover the pixel ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"p")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"))))),".\nAny stamps outside the segment have no chance to affect the pixel's color."),(0,m.kt)("p",null,"In order to get start and end points' positions, we have a geometry puzzle to solve:\n",(0,m.kt)("img",{alt:"func",src:t(8372).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"As the figure shows, we set up the same local coordinate as vanilla stroke in the shader program.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"p"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"p_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.625em",verticalAlign:"-0.1944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"p"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," as the origin and X and Y axes align to the tangent and normal direction,\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",separator:"true"},","),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"y"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"(x_p, y_p)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mpunct"},","),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," as the pixel's local coordinate.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"x")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"x")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x")))))," is the furthest point's x value.\nBy calculating it, we know the position of farthest point.\nThe distance from the pixel and the furthest point ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," is the radius of the furthest point, whose value is determined by x.\nTo solve the unknown ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"x")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"x")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"))))),", we can set an equation from the dashed right triangle ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mo",{parentName:"msup",stretchy:"false"},")"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mo",{parentName:"msup",stretchy:"false"},")"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"y"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)^2 = (x_p - x)^2 + y_p^2")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0641em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0641em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))))))),"."),(0,m.kt)("p",null,"It's not hard to derive ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mn",{parentName:"mrow"},"1"),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x) = r_1 * x/l + r_0 * (1-x/l)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6153em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6153em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},"1"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")")))))," from the figure below.\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"l")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"l")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l")))))," is the length of the current edge.\n",(0,m.kt)("img",{alt:"xradius",src:t(7637).Z,width:"2560",height:"1080"}),"\nBy replacing ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"r"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r(x)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"))))),", we get a quadratic equation ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"a"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msup"},"x"),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("mi",{parentName:"mrow"},"b"),(0,m.kt)("mi",{parentName:"mrow"},"x"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("mi",{parentName:"mrow"},"c"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mn",{parentName:"mrow"},"0")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"ax^2 + bx + c = 0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.8974em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"a"),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7778em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"b"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"c"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6444em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"0"))))),":"),(0,m.kt)("p",null,(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"a"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mn",{parentName:"mrow"},"1"),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msup",{parentName:"mrow"},(0,m.kt)("mrow",{parentName:"msup"},(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061")),(0,m.kt)("mn",{parentName:"msup"},"2")),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"a = 1 - \\cos^2\\theta")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"a"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7278em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"1"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.8141em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"))))),";\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"b"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("mn",{parentName:"mrow"},"2"),(0,m.kt)("mo",{parentName:"mrow"},"\u2217"),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8"),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"x"),(0,m.kt)("mi",{parentName:"msub"},"p")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"b = - 2 * (r_0\\cos\\theta + x_p)")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"b"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7278em",verticalAlign:"-0.0833em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mord"},"2"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2217"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0361em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"))))),";\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"c"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"x"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"+"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"y"),(0,m.kt)("mi",{parentName:"msubsup"},"p"),(0,m.kt)("mn",{parentName:"msubsup"},"2")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msubsup",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msubsup"},"r"),(0,m.kt)("mn",{parentName:"msubsup"},"0"),(0,m.kt)("mn",{parentName:"msubsup"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"c = x_p^2 + y_p^2 - r_0^2")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.4306em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"c"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal"},"x"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"0em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"+"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.1972em",verticalAlign:"-0.3831em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.03588em"}},"y"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.453em",marginLeft:"-0.0359em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3831em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.0622em",verticalAlign:"-0.2481em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8141em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.4519em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0"))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.063em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2481em"}},(0,m.kt)("span",{parentName:"span"}))))))))))),(0,m.kt)("p",null,"Remind that ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("mi",{parentName:"mrow"},"cos"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mi",{parentName:"mrow"},"\u03b8"),(0,m.kt)("mo",{parentName:"mrow"},"="),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},"("),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mo",{parentName:"mrow"},"\u2212"),(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mo",{parentName:"mrow",stretchy:"false"},")"),(0,m.kt)("mi",{parentName:"mrow",mathvariant:"normal"},"/"),(0,m.kt)("mi",{parentName:"mrow"},"l")),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"\\cos\\theta = (r_0 - r_1)/l")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.6944em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cos"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"\u03b8"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}}),(0,m.kt)("span",{parentName:"span",className:"mrel"},"="),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2778em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mopen"},"("),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}}),(0,m.kt)("span",{parentName:"span",className:"mbin"},"\u2212"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.2222em"}})),(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mclose"},")"),(0,m.kt)("span",{parentName:"span",className:"mord"},"/"),(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.01968em"}},"l"))))),".\nApplying the formula for solving quadratic equations,\ntwo roots of the equation are the X value of min and max points of the segment.\nTherefore, we know the range in the fragment shader and pixels only loop through stamps that can cover it."),(0,m.kt)(o,{showEditor:[!1,!0,!0],mdxType:"StampRound"}),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"texture",src:t(9602).Z,width:"512",height:"512"})),(0,m.kt)("p",null,"You can use RGBA values sampled from footprints directly, like what I did.\nBut for the most common scenario,\nwe set RGB values with users' brush setting and sample alpha values from a monochrome texture,\nwhose pixel's gray scale determines the opacity.\nThen apply random rotation and noise on each footprint to further stylize the stroke.\nThe stamp index values ",(0,m.kt)("inlineCode",{parentName:"p"},"currIndex")," are helpful to generate consistent random numbers as seed values."),(0,m.kt)("h2",{id:"square-footprint"},"Square footprint"),(0,m.kt)("p",null,"I introduce the brush rendering with the assumption that footprints are constrained within a dot area,\nwhich is the most common case in practice.\nBut it's not for all.\nArea outside the dot area plays a critical role sometimes.\nIf it is necessary to render square footprints, I guess experienced graphics developers can instantly get some ideas on\nnew approaches to placing the vertices and determining stamp positions.\nBut they may hurt the performance and have complex logic to implement."),(0,m.kt)("p",null,'I will introduce a very tricky approach to rendering the square footprint.\nMeanwhile, improve the rendering performance and reduce the complexity of the shader code.\nAll we have to sacrifice is the "correctness" of geometry, which is trivial for artists.'),(0,m.kt)("p",null,"We place vertices a little bit differently from before.\nInstead of offset vertices by ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0")),(0,m.kt)("mi",{parentName:"mrow"},"tan"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mfrac",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"mfrac"},"\u03b8"),(0,m.kt)("mn",{parentName:"mfrac"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0\\tan\\frac{\\theta}{2}")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.2251em",verticalAlign:"-0.345em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"tan"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mopen nulldelimiter"}),(0,m.kt)("span",{parentName:"span",className:"mfrac"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8801em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.655em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.23em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"frac-line",style:{borderBottomWidth:"0.04em"}})),(0,m.kt)("span",{parentName:"span",style:{top:"-3.394em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight",style:{marginRight:"0.02778em"}},"\u03b8"))))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.345em"}},(0,m.kt)("span",{parentName:"span"}))))),(0,m.kt)("span",{parentName:"span",className:"mclose nulldelimiter"}))))))," and ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1")),(0,m.kt)("mi",{parentName:"mrow"},"cot"),(0,m.kt)("mo",{parentName:"mrow"},"\u2061"),(0,m.kt)("mfrac",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"mfrac"},"\u03b8"),(0,m.kt)("mn",{parentName:"mfrac"},"2"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1\\cot\\frac{\\theta}{2}")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"1.2251em",verticalAlign:"-0.345em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"})))))),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mop"},"cot"),(0,m.kt)("span",{parentName:"span",className:"mspace",style:{marginRight:"0.1667em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mopen nulldelimiter"}),(0,m.kt)("span",{parentName:"span",className:"mfrac"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.8801em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.655em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"2")))),(0,m.kt)("span",{parentName:"span",style:{top:"-3.23em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"frac-line",style:{borderBottomWidth:"0.04em"}})),(0,m.kt)("span",{parentName:"span",style:{top:"-3.394em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"3em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight",style:{marginRight:"0.02778em"}},"\u03b8"))))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.345em"}},(0,m.kt)("span",{parentName:"span"}))))),(0,m.kt)("span",{parentName:"span",className:"mclose nulldelimiter"}))))))," along the normal direction,\nwe just offset ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," and ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," value, as the figure below shows."),(0,m.kt)("p",null,(0,m.kt)("img",{alt:"square",src:t(1889).Z,width:"2560",height:"1080"})),(0,m.kt)("p",null,"In the vertex shader or geometry shader, we put the radius value ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"0"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_0")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"0")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," to the left two vertices,\n",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mn",{parentName:"msub"},"1"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_1")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.3011em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mtight"},"1")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.15em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," to the right two vertices, and let fragment shader interpolate the values for us.\nThen we get an interpolated radius value ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," in the fragment shader.\nYou can prove this ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"}))))))))))," is the stroke radius value across the current pixel as the figure shows.\nWe assume the whole stroke is uni-radius and program the shader with ",(0,m.kt)("span",{parentName:"p",className:"math math-inline"},(0,m.kt)("span",{parentName:"span",className:"katex"},(0,m.kt)("span",{parentName:"span",className:"katex-mathml"},(0,m.kt)("math",{parentName:"span",xmlns:"http://www.w3.org/1998/Math/MathML"},(0,m.kt)("semantics",{parentName:"math"},(0,m.kt)("mrow",{parentName:"semantics"},(0,m.kt)("msub",{parentName:"mrow"},(0,m.kt)("mi",{parentName:"msub"},"r"),(0,m.kt)("mi",{parentName:"msub"},"p"))),(0,m.kt)("annotation",{parentName:"semantics",encoding:"application/x-tex"},"r_p")))),(0,m.kt)("span",{parentName:"span",className:"katex-html","aria-hidden":"true"},(0,m.kt)("span",{parentName:"span",className:"base"},(0,m.kt)("span",{parentName:"span",className:"strut",style:{height:"0.7167em",verticalAlign:"-0.2861em"}}),(0,m.kt)("span",{parentName:"span",className:"mord"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal",style:{marginRight:"0.02778em"}},"r"),(0,m.kt)("span",{parentName:"span",className:"msupsub"},(0,m.kt)("span",{parentName:"span",className:"vlist-t vlist-t2"},(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.1514em"}},(0,m.kt)("span",{parentName:"span",style:{top:"-2.55em",marginLeft:"-0.0278em",marginRight:"0.05em"}},(0,m.kt)("span",{parentName:"span",className:"pstrut",style:{height:"2.7em"}}),(0,m.kt)("span",{parentName:"span",className:"sizing reset-size6 size3 mtight"},(0,m.kt)("span",{parentName:"span",className:"mord mathnormal mtight"},"p")))),(0,m.kt)("span",{parentName:"span",className:"vlist-s"},"\u200b")),(0,m.kt)("span",{parentName:"span",className:"vlist-r"},(0,m.kt)("span",{parentName:"span",className:"vlist",style:{height:"0.2861em"}},(0,m.kt)("span",{parentName:"span"})))))))))),"."),(0,m.kt)(h,{showEditor:[!1,!0,!0],mdxType:"StampSquare"}),(0,m.kt)("p",null,"I basically cannot perceive the deformation caused by the trick at the first glance."))}w.isMDXComponent=!0},899:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/dotted-monkey-cf1ca1e77f00864b77d2fead263e3f4c.png"},8372:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-function-464d09193dd34a0055f5138889207f02.png"},2921:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-length-ea5d8a9b4e5cdf3e5441b2d53bbe14eb.png"},2054:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-position-1645edccb9edc05441593af193fc4ed0.png"},4962:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-range-f8e087a122c236bb0d125adf03d39a50.png"},1889:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-square-a31970ed55df23f105d4f72c416aba86.png"},775:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-to-stroke-082a5ddd80c45086b810ed8b9ebcea79.gif"},7637:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp-xradius-81321c92495722f37655ad34de62103b.png"},9602:(a,e,t)=>{"use strict";t.d(e,{Z:()=>s});const s=t.p+"assets/images/stamp86-ec89b059698b43632871fa23c95706eb.png"}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.b299749b.js b/assets/js/runtime~main.82d580af.js similarity index 89% rename from assets/js/runtime~main.b299749b.js rename to assets/js/runtime~main.82d580af.js index 9018115..1cb1790 100644 --- a/assets/js/runtime~main.b299749b.js +++ b/assets/js/runtime~main.82d580af.js @@ -1 +1 @@ -(()=>{"use strict";var e,t,r,o,a,n={},i={};function f(e){var t=i[e];if(void 0!==t)return t.exports;var r=i[e]={id:e,loaded:!1,exports:{}};return n[e].call(r.exports,r,r.exports,f),r.loaded=!0,r.exports}f.m=n,f.c=i,e=[],f.O=(t,r,o,a)=>{if(!r){var n=1/0;for(b=0;b=a)&&Object.keys(f.O).every((e=>f.O[e](r[u])))?r.splice(u--,1):(i=!1,a0&&e[b-1][2]>a;b--)e[b]=e[b-1];e[b]=[r,o,a]},f.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return f.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,f.t=function(e,o){if(1&o&&(e=this(e)),8&o)return e;if("object"==typeof e&&e){if(4&o&&e.__esModule)return e;if(16&o&&"function"==typeof e.then)return e}var a=Object.create(null);f.r(a);var n={};t=t||[null,r({}),r([]),r(r)];for(var i=2&o&&e;"object"==typeof i&&!~t.indexOf(i);i=r(i))Object.getOwnPropertyNames(i).forEach((t=>n[t]=()=>e[t]));return n.default=()=>e,f.d(a,n),a},f.d=(e,t)=>{for(var r in t)f.o(t,r)&&!f.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},f.f={},f.e=e=>Promise.all(Object.keys(f.f).reduce(((t,r)=>(f.f[r](e,t),t)),[])),f.u=e=>"assets/js/"+({53:"935f2afb",147:"26251b8b",195:"22948d09",342:"e7f20846",364:"ac092286",370:"ed94b18f",394:"5244698c",514:"1be78505",613:"6ae0415c",736:"d50c89f1",805:"bc662926",817:"14eb3368",915:"6858f5cd",918:"17896441",940:"a7b05c2e"}[e]||e)+"."+{53:"8f0f3cd0",147:"c8d8584b",195:"fa9be84a",298:"5414a50f",342:"4d519ff4",364:"c720d252",370:"9dbb1674",394:"83d2a283",514:"27794ed6",613:"b8eee0cc",736:"ecc88b7c",805:"017cc8fb",817:"1495867d",915:"caea66dd",918:"b90ff644",940:"fd1fd14f",972:"25a3f09a"}[e]+".js",f.miniCssF=e=>{},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o={},a="brush-stroke-tutorial:",f.l=(e,t,r,n)=>{if(o[e])o[e].push(t);else{var i,u;if(void 0!==r)for(var c=document.getElementsByTagName("script"),b=0;b{i.onerror=i.onload=null,clearTimeout(s);var a=o[e];if(delete o[e],i.parentNode&&i.parentNode.removeChild(i),a&&a.forEach((e=>e(r))),t)return t(r)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=l.bind(null,i.onerror),i.onload=l.bind(null,i.onload),u&&document.head.appendChild(i)}},f.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.p="/brush-rendering-tutorial/",f.gca=function(e){return e={17896441:"918","935f2afb":"53","26251b8b":"147","22948d09":"195",e7f20846:"342",ac092286:"364",ed94b18f:"370","5244698c":"394","1be78505":"514","6ae0415c":"613",d50c89f1:"736",bc662926:"805","14eb3368":"817","6858f5cd":"915",a7b05c2e:"940"}[e]||e,f.p+f.u(e)},(()=>{var e={303:0,532:0};f.f.j=(t,r)=>{var o=f.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var a=new Promise(((r,a)=>o=e[t]=[r,a]));r.push(o[2]=a);var n=f.p+f.u(t),i=new Error;f.l(n,(r=>{if(f.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var a=r&&("load"===r.type?"missing":r.type),n=r&&r.target&&r.target.src;i.message="Loading chunk "+t+" failed.\n("+a+": "+n+")",i.name="ChunkLoadError",i.type=a,i.request=n,o[1](i)}}),"chunk-"+t,t)}},f.O.j=t=>0===e[t];var t=(t,r)=>{var o,a,n=r[0],i=r[1],u=r[2],c=0;if(n.some((t=>0!==e[t]))){for(o in i)f.o(i,o)&&(f.m[o]=i[o]);if(u)var b=u(f)}for(t&&t(r);c{"use strict";var e,t,r,o,a,n={},i={};function f(e){var t=i[e];if(void 0!==t)return t.exports;var r=i[e]={id:e,loaded:!1,exports:{}};return n[e].call(r.exports,r,r.exports,f),r.loaded=!0,r.exports}f.m=n,f.c=i,e=[],f.O=(t,r,o,a)=>{if(!r){var n=1/0;for(b=0;b=a)&&Object.keys(f.O).every((e=>f.O[e](r[u])))?r.splice(u--,1):(i=!1,a0&&e[b-1][2]>a;b--)e[b]=e[b-1];e[b]=[r,o,a]},f.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return f.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,f.t=function(e,o){if(1&o&&(e=this(e)),8&o)return e;if("object"==typeof e&&e){if(4&o&&e.__esModule)return e;if(16&o&&"function"==typeof e.then)return e}var a=Object.create(null);f.r(a);var n={};t=t||[null,r({}),r([]),r(r)];for(var i=2&o&&e;"object"==typeof i&&!~t.indexOf(i);i=r(i))Object.getOwnPropertyNames(i).forEach((t=>n[t]=()=>e[t]));return n.default=()=>e,f.d(a,n),a},f.d=(e,t)=>{for(var r in t)f.o(t,r)&&!f.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},f.f={},f.e=e=>Promise.all(Object.keys(f.f).reduce(((t,r)=>(f.f[r](e,t),t)),[])),f.u=e=>"assets/js/"+({53:"935f2afb",147:"26251b8b",195:"22948d09",342:"e7f20846",364:"ac092286",370:"ed94b18f",394:"5244698c",514:"1be78505",613:"6ae0415c",736:"d50c89f1",805:"bc662926",817:"14eb3368",915:"6858f5cd",918:"17896441",940:"a7b05c2e"}[e]||e)+"."+{53:"8f0f3cd0",147:"c8d8584b",195:"fa9be84a",298:"5414a50f",342:"5a3c2c8d",364:"c720d252",370:"9dbb1674",394:"83d2a283",514:"27794ed6",613:"b8eee0cc",736:"ecc88b7c",805:"017cc8fb",817:"1495867d",915:"caea66dd",918:"b90ff644",940:"fd1fd14f",972:"25a3f09a"}[e]+".js",f.miniCssF=e=>{},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o={},a="brush-stroke-tutorial:",f.l=(e,t,r,n)=>{if(o[e])o[e].push(t);else{var i,u;if(void 0!==r)for(var c=document.getElementsByTagName("script"),b=0;b{i.onerror=i.onload=null,clearTimeout(s);var a=o[e];if(delete o[e],i.parentNode&&i.parentNode.removeChild(i),a&&a.forEach((e=>e(r))),t)return t(r)},s=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),u&&document.head.appendChild(i)}},f.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.p="/brush-rendering-tutorial/",f.gca=function(e){return e={17896441:"918","935f2afb":"53","26251b8b":"147","22948d09":"195",e7f20846:"342",ac092286:"364",ed94b18f:"370","5244698c":"394","1be78505":"514","6ae0415c":"613",d50c89f1:"736",bc662926:"805","14eb3368":"817","6858f5cd":"915",a7b05c2e:"940"}[e]||e,f.p+f.u(e)},(()=>{var e={303:0,532:0};f.f.j=(t,r)=>{var o=f.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var a=new Promise(((r,a)=>o=e[t]=[r,a]));r.push(o[2]=a);var n=f.p+f.u(t),i=new Error;f.l(n,(r=>{if(f.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var a=r&&("load"===r.type?"missing":r.type),n=r&&r.target&&r.target.src;i.message="Loading chunk "+t+" failed.\n("+a+": "+n+")",i.name="ChunkLoadError",i.type=a,i.request=n,o[1](i)}}),"chunk-"+t,t)}},f.O.j=t=>0===e[t];var t=(t,r)=>{var o,a,n=r[0],i=r[1],u=r[2],c=0;if(n.some((t=>0!==e[t]))){for(o in i)f.o(i,o)&&(f.m[o]=i[o]);if(u)var b=u(f)}for(t&&t(r);c Appendix | Brush Rendering Tutorial - + - + \ No newline at end of file diff --git a/category/basics/index.html b/category/basics/index.html index d495e78..89fae03 100644 --- a/category/basics/index.html +++ b/category/basics/index.html @@ -4,13 +4,13 @@ Basics | Brush Rendering Tutorial - + - + \ No newline at end of file diff --git a/index.html b/index.html index 01d18dd..2d83668 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Table of Contents | Brush Rendering Tutorial - + @@ -15,7 +15,7 @@ The pattern is achieved by setting the intervals between dots proportional to their radii. You will learn how to freely control stamp density along a stamp stroke. Very important for a serious project.

3D stroke

Learn how to extend the algorithms to 3D space.

I'm integrating it into the Blender Grease Pencil:

- + \ No newline at end of file