@@ -8,28 +8,32 @@ const chars = '%&#MHGw*+-. ';
8
8
// const chars = 'BS#&@$%*!:. ';
9
9
10
10
/**
11
- * Calculate the maximum rectangle that has an area less that maxChars and the same aspect ratio (width/height) as the image
11
+ * Calculate the maximum rectangle that has a surface area <= `maxSurface` and the same aspect ratio
12
12
*
13
13
* @param {number } width the image width
14
14
* @param {number } height the image heigth
15
- * @param {number } maxChars maximum number of chars allowed in Discord message
16
- * @param {number } heightMultiplier allows to scale the images height so it doesn't look stretched when after being converted to ASCII characters.
15
+ * @param {number } maxSurface maximum surface area of the output rectangle
17
16
* @returns
18
17
*/
19
- const calculateSize = (
20
- width ,
21
- height ,
22
- maxChars = 2000 ,
23
- heightMultiplier = 0.4 // TODO: calculate an accurate value, this value was just eyeballed
24
- ) => {
25
- const w = width ;
26
- const h = height * heightMultiplier ;
18
+ const calculateSize = ( width , height , maxSurface = 2000 ) => {
19
+ const imageArea = width * height ;
20
+
21
+ /*
22
+ We want to keep the aspect ratio of the image, this means that we must scale width and height by the same factor:
23
+ (1) newW = scaleFactor * width
24
+ (2) newH = scaleFactor * height
27
25
28
- const imageArea = w * h ;
29
- const scaleFactor = Math . sqrt ( maxChars / imageArea ) ;
26
+ We also want the are of the new image to be <= than maxSurface (we add 1 to width to account for the \n characters at the end of the lines)
27
+ (3) (newW + 1) * newH <= maxSurface
30
28
31
- const newW = Math . floor ( scaleFactor * w ) ;
32
- const newH = Math . floor ( scaleFactor * h ) ;
29
+ We substitute (1) and (2) into (3) and solve for `scaleFactor` to find the formula below.
30
+ */
31
+ const scaleFactor =
32
+ ( - height + Math . sqrt ( height * height + 4 * imageArea * maxSurface ) ) /
33
+ ( 2 * imageArea ) ;
34
+
35
+ const newW = Math . floor ( scaleFactor * width ) ;
36
+ const newH = Math . floor ( scaleFactor * height ) ;
33
37
return [ newW , newH ] ;
34
38
} ;
35
39
@@ -75,12 +79,15 @@ function groupLines(lineLength) {
75
79
export const urlToAscii = async (
76
80
url ,
77
81
originalWidth ,
78
- origianalHeight ,
82
+ originalHeight ,
79
83
maxChars
80
84
) => {
81
85
const [ width , height ] = calculateSize (
82
86
originalWidth ,
83
- origianalHeight ,
87
+
88
+ // Since pixels are square but character are not, we must squish the image so that it doesn't look stretched after being converted to ASCII
89
+ // TODO: calculate an accurate value, this value was just eyeballed
90
+ originalHeight * 0.4 ,
84
91
maxChars
85
92
) ;
86
93
0 commit comments