1010 * - position words inside a given area using a spiral placement algorithm (the actual function of this file used in the component @generateWordCloud)
1111 */
1212
13+ function getTightBoundingBox ( canvas , ctx ) {
14+ const { width, height } = canvas ;
15+ const img = ctx . getImageData ( 0 , 0 , width , height ) ;
16+ const data = img . data ;
17+ let minX = width , minY = height , maxX = 0 , maxY = 0 ;
18+ let found = false ;
19+ for ( let y = 0 ; y < height ; y += 1 ) {
20+ for ( let x = 0 ; x < width ; x += 1 ) {
21+ const alpha = data [ ( y * width + x ) * 4 + 3 ] ;
22+ if ( alpha > 1 ) {
23+ found = true ;
24+ if ( x < minX ) minX = x ;
25+ if ( x > maxX ) maxX = x ;
26+ if ( y < minY ) minY = y ;
27+ if ( y > maxY ) maxY = y ;
28+ }
29+ }
30+ }
31+ if ( ! found ) return [ 0 , 0 , 0 , 0 ] ;
32+ return [ minX , minY , maxX , maxY ] ;
33+ }
34+
1335/**
1436 * Generates a bitmap and mask representing a word, given font size and padding.
1537 * @param {Object } params
@@ -52,9 +74,11 @@ export function getWordBitmap({
5274 if ( data [ ( y * textW + x ) * 4 + 3 ] > 1 ) wordMask . push ( [ x , y ] ) ;
5375 }
5476 }
77+
78+ const [ minX , minY , maxX , maxY ] = getTightBoundingBox ( canvas , ctx ) ;
5579 ctx . restore ( ) ;
5680
57- return { w : textW , h : textH , wordMask } ;
81+ return { w : textW , h : textH , wordMask, minX , minY , maxX , maxY } ;
5882}
5983
6084/**
@@ -135,13 +159,14 @@ export function positionWords({
135159 words,
136160 proximity = 0 ,
137161 svg,
162+ strictPixelPadding
138163} ) {
139164 const { width, height } = svg ;
140165 const maskW = Math . round ( width ) ;
141166 const maskH = Math . round ( height ) ;
142167 const minFontSize = 1 ;
143168 const configMinFontSize = svg . minFontSize ;
144- const maxFontSize = svg . maxFontSize ;
169+ const maxFontSize = Math . min ( svg . maxFontSize , 100 ) ;
145170 const values = words . map ( w => w . value ) ;
146171 const minValue = Math . min ( ...values ) ;
147172 const maxValue = Math . max ( ...values ) ;
@@ -169,7 +194,7 @@ export function positionWords({
169194 let fontSize = targetFontSize ;
170195
171196 while ( ! placed && fontSize >= minFontSize ) {
172- let { w, h, wordMask } = getWordBitmap ( {
197+ let { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
173198 word : wordRaw ,
174199 fontSize,
175200 pad : proximity ,
@@ -178,7 +203,9 @@ export function positionWords({
178203 svg
179204 } ) ;
180205
181- wordMask = dilateWordMask ( { wordMask, w, h, dilation : 2 } ) ;
206+ if ( strictPixelPadding ) {
207+ wordMask = dilateWordMask ( { wordMask, w, h, dilation : 1 } ) ;
208+ }
182209
183210 let r = 0 ;
184211 let attempts = 0 ;
@@ -190,7 +217,7 @@ export function positionWords({
190217 const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
191218 if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
192219 if ( canPlaceAt ( { mask, maskW, maskH, wx :px , wy :py , wordMask} ) ) {
193- positionedWords . push ( { ...wordRaw , x : px - maskW / 2 , y : py - maskH / 2 , fontSize, width : w , height : h , angle : 0 } ) ;
220+ positionedWords . push ( { ...wordRaw , x : px - maskW / 2 , y : py - maskH / 2 , fontSize, width : w , height : h , angle : 0 , minX , minY , maxX , maxY } ) ;
194221 markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
195222 placed = true ;
196223 break ;
@@ -203,7 +230,7 @@ export function positionWords({
203230
204231 if ( ! placed && fontSize < minFontSize ) {
205232 fontSize = minFontSize ;
206- const { w, h, wordMask } = getWordBitmap ( {
233+ const { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
207234 word : wordRaw ,
208235 fontSize,
209236 pad : proximity ,
@@ -222,7 +249,7 @@ export function positionWords({
222249 const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
223250 if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
224251 if ( canPlaceAt ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ) {
225- positionedWords . push ( { ...wordRaw , x : px - maskW / 2 , y : py - maskH / 2 , fontSize, width : w , height : h , angle : 0 } ) ;
252+ positionedWords . push ( { ...wordRaw , x : px - maskW / 2 , y : py - maskH / 2 , fontSize, width : w , height : h , angle : 0 , minX , minY , maxX , maxY } ) ;
226253 markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
227254 placed = true ;
228255 break ;
0 commit comments