/// <summary> /// Compute a score for how good the wrapping is. /// </summary> /// <param name="words">Array of each word.</param> /// <param name="wordBreaks">Array of line breaks.</param> /// <param name="limit">Width to wrap each line.</param> /// <returns>Larger the better.</returns> private static double wrapScore_(string[] words, bool?[] wordBreaks, int limit) { // If this function becomes a performance liability, add caching. // Compute the length of each line. var lineLengths = new JsArray <int> { 0 }; var linePunctuation = new JsArray <string>(); for (var i = 0; i < words.Length; i++) { lineLengths[lineLengths.Length - 1] += words[i].Length; if (wordBreaks[i].HasValue && wordBreaks[i].Value == true) { lineLengths.Push(0); linePunctuation.Push(words[i].CharAt(words[i].Length - 1)); } else if (wordBreaks[i].HasValue && wordBreaks[i].Value == false) { lineLengths[lineLengths.Length - 1]++; } } var maxLength = lineLengths.Max(); var score = 0.0; for (var i = 0; i < lineLengths.Length; i++) { // Optimize for width. // -2 points per char over limit (scaled to the power of 1.5). score -= System.Math.Pow(System.Math.Abs(limit - lineLengths[i]), 1.5) * 2; // Optimize for even lines. // -1 point per char smaller than max (scaled to the power of 1.5). score -= System.Math.Pow(maxLength - lineLengths[i], 1.5); // Optimize for structure. // Add score to line endings after punctuation. if (i < linePunctuation.Length) { if (".?!".IndexOf(linePunctuation[i]) != -1) { score += limit / 3; } else if (",;)]}".IndexOf(linePunctuation[i]) != -1) { score += limit / 4; } } } // All else being equal, the last line should not be longer than the // previous line. For example, this looks wrong: // aaa bbb // ccc ddd eee if (lineLengths.Length > 1 && lineLengths[lineLengths.Length - 1] <= lineLengths[lineLengths.Length - 2]) { score += 0.5; } return(score); }