11import baseScale from '../scale/base' ;
22import programBuilder from '../program/programBuilder' ;
3- import lineShader from '../shaders/line/baseShader' ;
3+ import shaderBuilder , {
4+ vertexShaderBase ,
5+ fragmentShaderBase
6+ } from '../shaders/shaderBuilder' ;
47import drawModes from '../program/drawModes' ;
58import { rebind } from '@d3fc/d3fc-rebind' ;
69import lineWidthShader from '../shaders/lineWidth' ;
7- import * as vertexShaderSnippets from '../shaders/vertexShaderSnippets' ;
810import attribute from '../buffer/attribute' ;
911import elementIndices from '../buffer/elementIndices' ;
1012import types from '../buffer/types' ;
@@ -13,47 +15,112 @@ import rebindCurry from '../rebindCurry';
1315export default ( ) => {
1416 const program = programBuilder ( )
1517 . mode ( drawModes . TRIANGLES )
16- . subInstanceCount ( 12 ) ;
18+ . subInstanceCount ( 6 ) ;
1719 let xScale = baseScale ( ) ;
1820 let yScale = baseScale ( ) ;
1921 let decorate = ( ) => { } ;
2022 const lineWidth = lineWidthShader ( ) ;
2123
24+ /*
25+ Line segment from a to b has vertices A, B, C, D -
26+
27+ A |-------| B
28+ | \ |
29+ |a \ b|
30+ | \ |
31+ D |-------| C
32+
33+ |AD| = uStrokeWidth
34+ |AB| = |ab| + uStrokeWidth
35+
36+ Fragment shader implemented using line segment SDF
37+ simplified for starting at the origin (a) -
38+ https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
39+ */
2240 const cornerAttribute = attribute ( )
2341 . divisor ( 0 )
24- . size ( 3 )
42+ . size ( 4 )
2543 . type ( types . BYTE )
2644 . data ( [
27- [ - 1 , 0 , 0 ] ,
28- [ 1 , 1 , 0 ] ,
29- [ 1 , - 1 , 1 ] ,
30- [ - 1 , 0 , 1 ] ,
31- [ 1 , 1 , 1 ]
45+ [ - 1 , + 1 , 1 , 0 ] ,
46+ [ + 1 , + 1 , 0 , 1 ] ,
47+ [ + 1 , - 1 , 0 , 1 ] ,
48+ [ - 1 , - 1 , 1 , 0 ]
3249 ] ) ;
3350
3451 program
3552 . buffers ( )
36- . elementIndices ( elementIndices ( [ 0 , 1 , 2 , 1 , 2 , 3 , 0 , 2 , 3 , 2 , 3 , 4 ] ) )
53+ . elementIndices ( elementIndices ( [ 0 , 1 , 2 , 2 , 3 , 0 ] ) )
3754 . attribute ( 'aCorner' , cornerAttribute ) ;
3855
3956 const draw = numElements => {
40- const shaderBuilder = lineShader ( ) ;
41- program
42- . vertexShader ( shaderBuilder . vertex ( ) )
43- . fragmentShader ( shaderBuilder . fragment ( ) ) ;
44-
45- xScale ( program , 'prev' , 0 ) ;
46- yScale ( program , 'prev' , 1 ) ;
47- xScale ( program , 'curr' , 0 ) ;
48- yScale ( program , 'curr' , 1 ) ;
49- xScale ( program , 'gl_Position' , 0 ) ;
50- yScale ( program , 'gl_Position' , 1 ) ;
51- xScale ( program , 'nextNext' , 0 ) ;
52- yScale ( program , 'nextNext' , 1 ) ;
53-
54- program
55- . vertexShader ( )
56- . appendBody ( vertexShaderSnippets . postScaleLine . body ) ;
57+ const vertexShader = shaderBuilder ( vertexShaderBase ) ;
58+ const fragmentShader = shaderBuilder ( fragmentShaderBase ) ;
59+
60+ program . vertexShader ( vertexShader ) . fragmentShader ( fragmentShader ) ;
61+
62+ vertexShader . appendHeader ( `
63+ attribute vec4 aCorner;
64+ attribute float aCrossValue;
65+ attribute float aCrossNextValue;
66+ attribute float aMainValue;
67+ attribute float aMainNextValue;
68+ attribute float aDefined;
69+ attribute float aDefinedNext;
70+
71+ uniform float uStrokeWidth;
72+ uniform vec2 uScreen;
73+
74+ varying float vLength;
75+ varying vec2 vPosition;
76+ ` ) ;
77+
78+ vertexShader . appendBody ( `
79+ vec4 value = vec4(aCrossValue, aMainValue, 0.0, 1.0);
80+ vec4 nextValue = vec4(aCrossNextValue, aMainNextValue, 0.0, 1.0);
81+ ` ) ;
82+
83+ xScale ( program , 'value' , 0 ) ;
84+ xScale ( program , 'nextValue' , 0 ) ;
85+ yScale ( program , 'value' , 1 ) ;
86+ yScale ( program , 'nextValue' , 1 ) ;
87+
88+ vertexShader . appendBody ( `
89+ vec2 position = aCorner[2] * value.xy + aCorner[3] * nextValue.xy;
90+
91+ vec2 direction = normalize((nextValue.xy - value.xy) * uScreen);
92+ vec2 normal = vec2(direction.y, -direction.x);
93+ vec2 padding = ((uStrokeWidth / 2.0) / (uScreen / 2.0));
94+
95+ padding *= aDefined * aDefinedNext;
96+ position += (aCorner[0] * direction + aCorner[1] * normal) * padding;
97+
98+ gl_Position = vec4(position.x, position.y, 0.0, 1.0);
99+
100+ vLength = length((nextValue.xy - value.xy) * (uScreen / 2.0));
101+ vPosition = aCorner.xy * (uStrokeWidth / 2.0);
102+ vPosition.x += aCorner[3] * vLength;
103+ ` ) ;
104+
105+ // all fragment shader inputs are pixel denominated
106+
107+ fragmentShader . appendHeader ( `
108+ uniform float uStrokeWidth;
109+ varying float vLength;
110+ varying vec2 vPosition;
111+
112+ float canFill = 0.0;
113+ float canStroke = 1.0;
114+ ` ) ;
115+
116+ fragmentShader . appendBody ( `
117+ vec2 position = vPosition;
118+ position.x -= clamp(position.x, 0.0, vLength);
119+ float sdf = length(position) - uStrokeWidth / 2.0;
120+ if (sdf > 0.5) {
121+ discard;
122+ }
123+ ` ) ;
57124
58125 lineWidth ( program ) ;
59126
@@ -88,13 +155,6 @@ export default () => {
88155
89156 rebind ( draw , program , 'context' , 'pixelRatio' ) ;
90157 rebind ( draw , lineWidth , 'lineWidth' ) ;
91- rebindCurry (
92- draw ,
93- 'crossPreviousValueAttribute' ,
94- program . buffers ( ) ,
95- 'attribute' ,
96- 'aCrossPrevValue'
97- ) ;
98158 rebindCurry (
99159 draw ,
100160 'crossValueAttribute' ,
@@ -109,20 +169,6 @@ export default () => {
109169 'attribute' ,
110170 'aCrossNextValue'
111171 ) ;
112- rebindCurry (
113- draw ,
114- 'crossNextNextValueAttribute' ,
115- program . buffers ( ) ,
116- 'attribute' ,
117- 'aCrossNextNextValue'
118- ) ;
119- rebindCurry (
120- draw ,
121- 'mainPreviousValueAttribute' ,
122- program . buffers ( ) ,
123- 'attribute' ,
124- 'aMainPrevValue'
125- ) ;
126172 rebindCurry (
127173 draw ,
128174 'mainValueAttribute' ,
@@ -137,13 +183,6 @@ export default () => {
137183 'attribute' ,
138184 'aMainNextValue'
139185 ) ;
140- rebindCurry (
141- draw ,
142- 'mainNextNextValueAttribute' ,
143- program . buffers ( ) ,
144- 'attribute' ,
145- 'aMainNextNextValue'
146- ) ;
147186 rebindCurry (
148187 draw ,
149188 'definedAttribute' ,
0 commit comments