Simplify the layout algorithm

This commit is contained in:
Hojjat 2022-12-19 11:54:19 -07:00 • committed by Jeremy Soller
parent 1bc476c06e
commit a4ed73b664

View file

@ -629,190 +629,113 @@ impl ShapeLine {
// that fits on a line. // that fits on a line.
let mut current_visual_line = Vec::with_capacity(1); let mut current_visual_line = Vec::with_capacity(1);
for span_index in 0..self.spans.len() { let mut fit_x = line_width as f32;
let span = &self.spans[span_index];
let mut word_ranges = Vec::new(); for (span_index, span) in self.spans.iter().enumerate() {
if self.rtl != span.level.is_rtl() { let mut word_ranges: Vec<(Range<usize>, f32)> = Vec::new();
let mut fit_x = x; let mut word_range_width = 0.;
let mut fitting_end = span.words.len();
if !span.words.is_empty() {
let mut i = span.words.len()-1;
loop {
let word = &span.words[i];
let word_size = font_size as f32 * word.x_advance;
let wrap = if self.rtl { // Create the word ranges that fits in a visual line
fit_x - word_size < end_x if self.rtl != span.level.is_rtl() { // incongruent directions
let mut fitting_start = span.words.len();
for (i, word) in span.words.iter().enumerate().rev() {
let word_size = font_size as f32 * word.x_advance;
if fit_x - word_size >= 0. { // fits
fit_x -= word_size;
word_range_width += word_size;
continue;
} else {
word_ranges.push((i+1..fitting_start, word_range_width));
if word.blank {
fit_x = line_width as f32;
word_range_width = 0.;
fitting_start = i + 1;
} else { } else {
fit_x + word_size > end_x fit_x = line_width as f32 - word_size;
}; word_range_width = word_size;
fitting_start = i+1;
if wrap {
let mut fitting_start = i + 1;
if fitting_start == fitting_end { //long single word
fitting_start -= 1;
}
while fitting_start < fitting_end {
if span.words[fitting_start].blank {
fitting_start += 1;
} else {
break;
}
}
word_ranges.push((fitting_start..fitting_end, true));
// This is here to handle a single long word
if word_size > line_width as f32 && fitting_start != i {
word_ranges.push((i..i+1, true));
fitting_end = i;
break;
}
fitting_end = i;
fit_x = start_x;
if i == 0 {
break;
}
i -= 1;
continue;
}
if self.rtl {
fit_x -= word_size;
} else {
fit_x += word_size;
}
if i == 0 {
break;
}
i -= 1;
}
}
if !word_ranges.is_empty() {
while fitting_end > 0 {
if span.words[fitting_end - 1].blank {
fitting_end -= 1;
} else {
break;
} }
} }
} }
if fitting_end > 0 { word_ranges.push((0..fitting_start, word_range_width));
word_ranges.push((0..fitting_end, false));
} } else { // congruent direction
} else {
let mut fit_x = x;
let mut fitting_start = 0; let mut fitting_start = 0;
if !span.words.is_empty() { for (i, word) in span.words.iter().enumerate() {
let mut i = 0; let word_size = font_size as f32 * word.x_advance;
loop { if fit_x - word_size >= 0. { // fits
let word = &span.words[i];
let word_size = font_size as f32 * word.x_advance;
let wrap = if self.rtl { fit_x -= word_size;
fit_x - word_size < end_x word_range_width += word_size;
continue;
} else {
word_ranges.push((fitting_start..i, word_range_width));
if word.blank {
fit_x = line_width as f32;
word_range_width = 0.;
fitting_start = i + 1;
} else { } else {
fit_x + word_size > end_x fit_x = line_width as f32 - word_size;
}; word_range_width = word_size;
if wrap {
if fitting_start == i { // One word is bigger than the linewidth
i += 1;
}
word_ranges.push((fitting_start..i, true));
if let Some(next_word) = &span.words.get(i) {
if next_word.blank {
i += 1;
}
}
fitting_start = i; fitting_start = i;
fit_x = start_x;
if i >= span.words.len() {
break;
}
continue;
}
if self.rtl {
fit_x -= word_size;
} else {
fit_x += word_size;
}
i += 1;
if i >= span.words.len() {
break;
} }
} }
} }
if fitting_start < span.words.len() { word_ranges.push((fitting_start..span.words.len(), word_range_width));
word_ranges.push((fitting_start..span.words.len(), false));
}
} }
// Calculate the actual size // Create a visual line
let mut wrapped; for (range, word_range_width) in word_ranges {
for (range, wrap) in word_ranges { // To simplify the algorithm above, we might push empty ranges but we ignore them here
// This is used to avoid creating an empty line if the word if range.len() == 0 {
// causing the line break is very long itself continue;
// we should change the algorithm to not need this }
wrapped = wrap;
for word_index in range.clone() {
let word = &span.words[word_index];
let word_size = font_size as f32 * word.x_advance;
let word_wrap = if self.rtl { let (span_x_advance, span_y_advance) = span.words[range.clone()]
x - word_size < end_x .iter()
.fold((0., 0.), |sum, word| {
(sum.0 + font_size as f32 * word.x_advance, sum.0 + font_size as f32 * word.y_advance)
});
let fits = !if self.rtl {
x - word_range_width < end_x
} else {
x + word_range_width > end_x
};
if fits {
current_visual_line.push((span_index, range.clone()));
if self.rtl {
x -= word_range_width;
} else { } else {
x + word_size > end_x x += word_range_width;
}; }
if word_wrap && !wrap_simple { y += span_y_advance;
if range.len() == 1 && !current_visual_line.is_empty(){ } else {
vl_range_of_spans.push(current_visual_line); if !current_visual_line.is_empty(){
current_visual_line = Vec::with_capacity(1);
wrapped = false;
}
current_visual_line.push((span_index, range.clone()));
vl_range_of_spans.push(current_visual_line); vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1); current_visual_line = Vec::with_capacity(1);
x = start_x; x = start_x;
y = 0.0; y = 0.0;
continue;
} }
current_visual_line.push((span_index, range.clone()));
if self.rtl { if self.rtl {
x -= word_size; x -= word_range_width;
} else { } else {
x += word_size; x += word_range_width;
} }
y += font_size as f32 * word.y_advance; y += span_y_advance;
} if span_x_advance > line_width as f32 { // single word is bigger than line_width
vl_range_of_spans.push(current_visual_line);
if let Some(v) = vl_range_of_spans.last() { current_visual_line = Vec::with_capacity(1);
if let Some((s,r)) = v.last() { x = start_x;
if *s == span_index && *r == range { y = 0.0;
// this avoid duplicating if the range alrady is pushed
// we should change the algorithm to not need to check
// for the last range
} else {
current_visual_line.push((span_index, range));
}
} else {
current_visual_line.push((span_index, range));
} }
} else {
current_visual_line.push((span_index, range));
}
if wrapped && !current_visual_line.is_empty(){
vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1);
x = start_x;
y = 0.0;
} }
} }
} }
@ -821,6 +744,7 @@ impl ShapeLine {
vl_range_of_spans.push(current_visual_line); vl_range_of_spans.push(current_visual_line);
} }
// Create the LayoutLines using the ranges inside visual lines
for visual_line in &vl_range_of_spans { for visual_line in &vl_range_of_spans {
let new_order = self.reorder(visual_line); let new_order = self.reorder(visual_line);
let mut glyphs = Vec::with_capacity(1); let mut glyphs = Vec::with_capacity(1);