@@ -40,7 +40,11 @@ export default async function spatial(interpolate) {
4040}
4141
4242export async function barycentric ( ) {
43- return spatial ( interpolateBarycentric ) ;
43+ return spatial ( interpolateBarycentric ( false ) ) ;
44+ }
45+
46+ export async function barycentricExtra ( ) {
47+ return spatial ( interpolateBarycentric ( true ) ) ;
4448}
4549
4650export async function voronoi ( ) {
@@ -65,48 +69,98 @@ function interpolateVoronoi(index, canvas, {X, Y, R, G, B, FO}, {r, g, b, a}) {
6569 }
6670}
6771
68- function interpolateBarycentric ( index , canvas , { X, Y, R, G, B, FO } , { r, g, b, a} ) {
69- const { width, height} = canvas ;
70- const context2d = canvas . getContext ( "2d" ) ;
71- const image = context2d . createImageData ( width , height ) ;
72- const imageData = image . data ;
72+ function interpolateBarycentric ( extrapolate = true ) {
73+ return ( index , canvas , { X, Y, R, G, B, FO } , { r, g, b, a} ) => {
74+ const { width, height} = canvas ;
75+ const context2d = canvas . getContext ( "2d" ) ;
76+ const image = context2d . createImageData ( width , height ) ;
77+ const imageData = image . data ;
7378
74- const delaunay = d3 . Delaunay . from (
75- index ,
76- ( i ) => X [ i ] ,
77- ( i ) => Y [ i ]
78- ) ;
79- const { points, triangles} = delaunay ;
79+ // renumber/reindex everything, because we're going to add points if extrapolate is true
80+ X = Array . from ( index , ( i ) => X [ i ] ) ;
81+ Y = Array . from ( index , ( i ) => Y [ i ] ) ;
82+ R = R && Array . from ( index , ( i ) => R [ i ] ) ;
83+ G = G && Array . from ( index , ( i ) => G [ i ] ) ;
84+ B = B && Array . from ( index , ( i ) => B [ i ] ) ;
85+ FO = FO && Array . from ( index , ( i ) => FO [ i ] ) ;
86+ index = d3 . range ( index . length ) ;
8087
81- // Interpolate the interior of all triangles with barycentric coordinates
82- for ( let i = 0 ; i < triangles . length ; i += 3 ) {
83- const T = triangles . subarray ( i , i + 3 ) ;
84- let [ ia , ib , ic ] = Array . from ( T , ( i ) => index [ i ] ) ;
88+ // to extrapolate, we need to fill the rectangle; pad the perimeter with vertices all around.
89+ if ( extrapolate ) {
90+ let i = 1 + index . length ;
91+ const addPoint = ( x , y ) => {
92+ ( X [ i ] = x ) , ( Y [ i ] = y ) , ( R [ i ] = NaN ) , index . push ( i ++ ) ;
93+ } ;
94+ for ( let k = 0 ; k < 1.01 ; k += 0.01 ) {
95+ addPoint ( k * width , - 1 ) ;
96+ addPoint ( ( 1 - k ) * width , height + 1 ) ;
97+ addPoint ( - 1 , k * height ) ;
98+ addPoint ( width + 1 , ( 1 - k ) * height ) ;
99+ }
100+ }
85101
86- const [ Ax , Bx , Cx ] = Array . from ( T , ( i ) => points [ 2 * i ] ) ;
87- const [ Ay , By , Cy ] = Array . from ( T , ( i ) => points [ 2 * i + 1 ] ) ;
88- const [ x0 , x1 ] = d3 . extent ( [ Ax , Bx , Cx ] ) ;
89- const [ y0 , y1 ] = d3 . extent ( [ Ay , By , Cy ] ) ;
102+ const delaunay = d3 . Delaunay . from (
103+ index ,
104+ ( i ) => X [ i ] ,
105+ ( i ) => Y [ i ]
106+ ) ;
107+ const { points, triangles} = delaunay ;
108+
109+ // two rounds of extrapolation are necessary; the first fills the triangles
110+ // which have one unknown dot, the second fills the remaining triangles
111+ if ( extrapolate ) {
112+ for ( let c = 0 ; c < 2 ; c ++ ) {
113+ for ( let i = 0 ; i < triangles . length ; i += 3 ) {
114+ const [ a , b , c ] = triangles . subarray ( i , i + 3 ) ;
115+ if ( isNaN ( R [ index [ a ] ] ) ) {
116+ R [ index [ a ] ] = ( R [ index [ b ] ] + R [ index [ c ] ] ) / 2 ;
117+ G [ index [ a ] ] = ( G [ index [ b ] ] + G [ index [ c ] ] ) / 2 ;
118+ B [ index [ a ] ] = ( B [ index [ b ] ] + B [ index [ c ] ] ) / 2 ;
119+ }
120+ if ( isNaN ( R [ index [ b ] ] ) ) {
121+ R [ index [ b ] ] = ( R [ index [ c ] ] + R [ index [ a ] ] ) / 2 ;
122+ G [ index [ b ] ] = ( G [ index [ c ] ] + G [ index [ a ] ] ) / 2 ;
123+ B [ index [ b ] ] = ( B [ index [ c ] ] + B [ index [ a ] ] ) / 2 ;
124+ }
125+ if ( isNaN ( R [ index [ c ] ] ) ) {
126+ R [ index [ c ] ] = ( R [ index [ a ] ] + R [ index [ b ] ] ) / 2 ;
127+ G [ index [ c ] ] = ( G [ index [ a ] ] + G [ index [ b ] ] ) / 2 ;
128+ B [ index [ c ] ] = ( B [ index [ a ] ] + B [ index [ b ] ] ) / 2 ;
129+ }
130+ }
131+ }
132+ }
90133
91- const z = ( By - Cy ) * ( Ax - Cx ) + ( Ay - Cy ) * ( Cx - Bx ) ;
92- if ( ! z ) continue ;
134+ // Interpolate the interior of all triangles with barycentric coordinates
135+ for ( let i = 0 ; i < triangles . length ; i += 3 ) {
136+ const T = triangles . subarray ( i , i + 3 ) ;
137+ let [ ia , ib , ic ] = Array . from ( T , ( i ) => index [ i ] ) ;
93138
94- for ( let x = Math . floor ( x0 ) ; x < x1 ; x ++ ) {
95- for ( let y = Math . floor ( y0 ) ; y < y1 ; y ++ ) {
96- if ( x < 0 || x >= width || y < 0 || y >= height ) continue ;
97- const ga = ( ( By - Cy ) * ( x - Cx ) + ( y - Cy ) * ( Cx - Bx ) ) / z ;
98- if ( ga < 0 ) continue ;
99- const gb = ( ( Cy - Ay ) * ( x - Cx ) + ( y - Cy ) * ( Ax - Cx ) ) / z ;
100- if ( gb < 0 ) continue ;
101- const gc = 1 - ga - gb ;
102- if ( gc < 0 ) continue ;
103- const k = ( x + width * y ) << 2 ;
104- imageData [ k + 0 ] = R ? ga * R [ ia ] + gb * R [ ib ] + gc * R [ ic ] : r ;
105- imageData [ k + 1 ] = G ? ga * G [ ia ] + gb * G [ ib ] + gc * G [ ic ] : g ;
106- imageData [ k + 2 ] = B ? ga * B [ ia ] + gb * B [ ib ] + gc * B [ ic ] : b ;
107- imageData [ k + 3 ] = FO ? ( ga * FO [ ia ] + gb * FO [ ib ] + gc * FO [ ic ] ) * 255 : a ;
139+ const [ Ax , Bx , Cx ] = Array . from ( T , ( i ) => points [ 2 * i ] ) ;
140+ const [ Ay , By , Cy ] = Array . from ( T , ( i ) => points [ 2 * i + 1 ] ) ;
141+ const [ x0 , x1 ] = d3 . extent ( [ Ax , Bx , Cx ] ) ;
142+ const [ y0 , y1 ] = d3 . extent ( [ Ay , By , Cy ] ) ;
143+
144+ const z = ( By - Cy ) * ( Ax - Cx ) + ( Ay - Cy ) * ( Cx - Bx ) ;
145+ if ( ! z ) continue ;
146+
147+ for ( let x = Math . floor ( x0 ) ; x < x1 ; x ++ ) {
148+ for ( let y = Math . floor ( y0 ) ; y < y1 ; y ++ ) {
149+ if ( x < 0 || x >= width || y < 0 || y >= height ) continue ;
150+ const ga = ( ( By - Cy ) * ( x - Cx ) + ( y - Cy ) * ( Cx - Bx ) ) / z ;
151+ if ( ga < 0 ) continue ;
152+ const gb = ( ( Cy - Ay ) * ( x - Cx ) + ( y - Cy ) * ( Ax - Cx ) ) / z ;
153+ if ( gb < 0 ) continue ;
154+ const gc = 1 - ga - gb ;
155+ if ( gc < 0 ) continue ;
156+ const k = ( x + width * y ) << 2 ;
157+ imageData [ k + 0 ] = R ? ga * R [ ia ] + gb * R [ ib ] + gc * R [ ic ] : r ;
158+ imageData [ k + 1 ] = G ? ga * G [ ia ] + gb * G [ ib ] + gc * G [ ic ] : g ;
159+ imageData [ k + 2 ] = B ? ga * B [ ia ] + gb * B [ ib ] + gc * B [ ic ] : b ;
160+ imageData [ k + 3 ] = FO ? ( ga * FO [ ia ] + gb * FO [ ib ] + gc * FO [ ic ] ) * 255 : a ;
161+ }
108162 }
109163 }
110- }
111- context2d . putImageData ( image , 0 , 0 ) ;
164+ context2d . putImageData ( image , 0 , 0 ) ;
165+ } ;
112166}
0 commit comments