-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmart_word_wrap.rb
132 lines (102 loc) · 2.94 KB
/
smart_word_wrap.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/ruby
# Author: Daniel "Trizen" Șuteu
# License: GPLv3
# Date: 15th October 2013
# https://trizenx.blogspot.com
# https://trizenx.blogspot.com/2013/11/smart-word-wrap.html
# Smart word wrap algorithm
# See: https://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
class SmartWordWrap
@width = 0;
# This is the ugliest method! It, recursively,
# prepares the words for the combine() function.
def prepare_words(array)
root = []
len = 0
i = -1
limit = array.size-1
while ((i+=1) <= limit)
len += (word_len = array[i].size)
if len > @width
if word_len > @width
len -= word_len
value = array[i]
array.delete_at(i)
array.insert(i, *(value.scan(/.{1,#{@width}}/m)))
limit = array.size-1
i -= 1; next
end
break
end
root << [
array[0..i].join(' '),
prepare_words(array[i+1 .. limit])
]
break if ((len += 1) >= @width)
end
root
end
# This function combines the
# the parents with the childrens.
def combine(root, path)
row = []
key = path.shift
path.each do |value|
root << key
if value.empty?
row = [root + []]
else
value.each do |item|
row += combine(root, item)
end
end
root.pop
end
row
end
# This function finds the best
# combination available and returns it.
def find_best(arrays)
best = {
score: Float::INFINITY,
value: [],
}
arrays.each { |array|
score = 0;
array.each { |line|
score += (@width - line.size)**2
}
if score < best[:score]
best[:score] = score
best[:value] = array
end
}
best[:value]
end
# This is the main function of the algorithm
# which calls all the other functions and
# returns the best possible wrapped string.
def smart_wrap(text, width)
@width = width;
words = text.is_a?(Enumerable) ? text : text.split(' ')
lines = [];
self.prepare_words(words).each { |path|
lines += combine([], path)
}
best = self.find_best(lines)
best == nil and return nil
return best.join("\n")
end
end
#
## Usage examples
#
text = 'aaa bb cc ddddd'
obj = SmartWordWrap.new
puts obj.smart_wrap(text, 6)
puts '-'*80
text = 'As shown in the above phases (or steps), the algorithm does many useless transformations'
puts obj.smart_wrap(text, 20)
puts '-'*80
text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
puts obj.smart_wrap(text, 20)