10
10
* - position words inside a given area using a spiral placement algorithm (the actual function of this file used in the component @generateWordCloud)
11
11
*/
12
12
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
+
13
35
/**
14
36
* Generates a bitmap and mask representing a word, given font size and padding.
15
37
* @param {Object } params
@@ -52,9 +74,11 @@ export function getWordBitmap({
52
74
if ( data [ ( y * textW + x ) * 4 + 3 ] > 1 ) wordMask . push ( [ x , y ] ) ;
53
75
}
54
76
}
77
+
78
+ const [ minX , minY , maxX , maxY ] = getTightBoundingBox ( canvas , ctx ) ;
55
79
ctx . restore ( ) ;
56
80
57
- return { w : textW , h : textH , wordMask } ;
81
+ return { w : textW , h : textH , wordMask, minX , minY , maxX , maxY } ;
58
82
}
59
83
60
84
/**
@@ -170,7 +194,7 @@ export function positionWords({
170
194
let fontSize = targetFontSize ;
171
195
172
196
while ( ! placed && fontSize >= minFontSize ) {
173
- let { w, h, wordMask } = getWordBitmap ( {
197
+ let { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
174
198
word : wordRaw ,
175
199
fontSize,
176
200
pad : proximity ,
@@ -193,7 +217,7 @@ export function positionWords({
193
217
const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
194
218
if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
195
219
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 } ) ;
197
221
markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
198
222
placed = true ;
199
223
break ;
@@ -206,7 +230,7 @@ export function positionWords({
206
230
207
231
if ( ! placed && fontSize < minFontSize ) {
208
232
fontSize = minFontSize ;
209
- const { w, h, wordMask } = getWordBitmap ( {
233
+ const { w, h, wordMask, minX , minY , maxX , maxY } = getWordBitmap ( {
210
234
word : wordRaw ,
211
235
fontSize,
212
236
pad : proximity ,
@@ -225,7 +249,7 @@ export function positionWords({
225
249
const py = Math . round ( cy + r * Math . sin ( theta * Math . PI / 180 ) - h / 2 ) ;
226
250
if ( px < 0 || py < 0 || px + w > maskW || py + h > maskH ) continue ;
227
251
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 } ) ;
229
253
markMask ( { mask, maskW, maskH, wx : px , wy : py , wordMask } ) ;
230
254
placed = true ;
231
255
break ;
0 commit comments