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/**
@@ -170,7 +194,7 @@ export function positionWords({
170194 let fontSize = targetFontSize ;
171195
172196 while ( ! placed && fontSize >= minFontSize ) {
173- let { w, h, wordMask } = getWordBitmap ( {
197+ let { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
174198 word : wordRaw ,
175199 fontSize,
176200 pad : proximity ,
@@ -193,7 +217,7 @@ export function positionWords({
193217 const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
194218 if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
195219 if ( canPlaceAt ( { mask, maskW, maskH, wx :px , wy :py , wordMask} ) ) {
196- 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 } ) ;
197221 markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
198222 placed = true ;
199223 break ;
@@ -206,7 +230,7 @@ export function positionWords({
206230
207231 if ( ! placed && fontSize < minFontSize ) {
208232 fontSize = minFontSize ;
209- const { w, h, wordMask } = getWordBitmap ( {
233+ const { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
210234 word : wordRaw ,
211235 fontSize,
212236 pad : proximity ,
@@ -225,7 +249,7 @@ export function positionWords({
225249 const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
226250 if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
227251 if ( canPlaceAt ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ) {
228- 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 } ) ;
229253 markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
230254 placed = true ;
231255 break ;
0 commit comments