1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! Geometry in flow-relative space.
6 
7 use crate::properties::style_structs;
8 use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
9 use euclid::num::Zero;
10 use std::cmp::{max, min};
11 use std::fmt::{self, Debug, Error, Formatter};
12 use std::ops::{Add, Sub};
13 use unicode_bidi as bidi;
14 
15 pub enum BlockFlowDirection {
16     TopToBottom,
17     RightToLeft,
18     LeftToRight,
19 }
20 
21 pub enum InlineBaseDirection {
22     LeftToRight,
23     RightToLeft,
24 }
25 
26 // TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()
27 bitflags!(
28     #[cfg_attr(feature = "servo", derive(MallocSizeOf, Serialize))]
29     #[repr(C)]
30     pub struct WritingMode: u8 {
31         /// A vertical writing mode; writing-mode is vertical-rl,
32         /// vertical-lr, sideways-lr, or sideways-rl.
33         const VERTICAL = 1 << 0;
34         /// The inline flow direction is reversed against the physical
35         /// direction (i.e. right-to-left or bottom-to-top); writing-mode is
36         /// sideways-lr or direction is rtl (but not both).
37         ///
38         /// (This bit can be derived from the others, but we store it for
39         /// convenience.)
40         const INLINE_REVERSED = 1 << 1;
41         /// A vertical writing mode whose block progression direction is left-
42         /// to-right; writing-mode is vertical-lr or sideways-lr.
43         ///
44         /// Never set without VERTICAL.
45         const VERTICAL_LR = 1 << 2;
46         /// The line-over/line-under sides are inverted with respect to the
47         /// block-start/block-end edge; writing-mode is vertical-lr.
48         ///
49         /// Never set without VERTICAL and VERTICAL_LR.
50         const LINE_INVERTED = 1 << 3;
51         /// direction is rtl.
52         const RTL = 1 << 4;
53         /// All text within a vertical writing mode is displayed sideways
54         /// and runs top-to-bottom or bottom-to-top; set in these cases:
55         ///
56         /// * writing-mode: sideways-rl;
57         /// * writing-mode: sideways-lr;
58         ///
59         /// Never set without VERTICAL.
60         const VERTICAL_SIDEWAYS = 1 << 5;
61         /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;
62         /// set in these cases:
63         ///
64         /// * writing-mode: vertical-rl; text-orientation: sideways;
65         /// * writing-mode: vertical-lr; text-orientation: sideways;
66         ///
67         /// Never set without VERTICAL.
68         const TEXT_SIDEWAYS = 1 << 6;
69         /// Horizontal text within a vertical writing mode is displayed with each
70         /// glyph upright; set in these cases:
71         ///
72         /// * writing-mode: vertical-rl; text-orientation: upright;
73         /// * writing-mode: vertical-lr: text-orientation: upright;
74         ///
75         /// Never set without VERTICAL.
76         const UPRIGHT = 1 << 7;
77     }
78 );
79 
80 impl WritingMode {
81     /// Return a WritingMode bitflags from the relevant CSS properties.
new(inheritedbox_style: &style_structs::InheritedBox) -> Self82     pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
83         use crate::properties::longhands::direction::computed_value::T as Direction;
84         use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
85 
86         let mut flags = WritingMode::empty();
87 
88         let direction = inheritedbox_style.clone_direction();
89         let writing_mode = inheritedbox_style.clone_writing_mode();
90 
91         match direction {
92             Direction::Ltr => {},
93             Direction::Rtl => {
94                 flags.insert(WritingMode::RTL);
95             },
96         }
97 
98         match writing_mode {
99             SpecifiedWritingMode::HorizontalTb => {
100                 if direction == Direction::Rtl {
101                     flags.insert(WritingMode::INLINE_REVERSED);
102                 }
103             },
104             SpecifiedWritingMode::VerticalRl => {
105                 flags.insert(WritingMode::VERTICAL);
106                 if direction == Direction::Rtl {
107                     flags.insert(WritingMode::INLINE_REVERSED);
108                 }
109             },
110             SpecifiedWritingMode::VerticalLr => {
111                 flags.insert(WritingMode::VERTICAL);
112                 flags.insert(WritingMode::VERTICAL_LR);
113                 flags.insert(WritingMode::LINE_INVERTED);
114                 if direction == Direction::Rtl {
115                     flags.insert(WritingMode::INLINE_REVERSED);
116                 }
117             },
118             #[cfg(feature = "gecko")]
119             SpecifiedWritingMode::SidewaysRl => {
120                 flags.insert(WritingMode::VERTICAL);
121                 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
122                 if direction == Direction::Rtl {
123                     flags.insert(WritingMode::INLINE_REVERSED);
124                 }
125             },
126             #[cfg(feature = "gecko")]
127             SpecifiedWritingMode::SidewaysLr => {
128                 flags.insert(WritingMode::VERTICAL);
129                 flags.insert(WritingMode::VERTICAL_LR);
130                 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
131                 if direction == Direction::Ltr {
132                     flags.insert(WritingMode::INLINE_REVERSED);
133                 }
134             },
135         }
136 
137         #[cfg(feature = "gecko")]
138         {
139             use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
140 
141             // text-orientation only has an effect for vertical-rl and
142             // vertical-lr values of writing-mode.
143             match writing_mode {
144                 SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => {
145                     match inheritedbox_style.clone_text_orientation() {
146                         TextOrientation::Mixed => {},
147                         TextOrientation::Upright => {
148                             flags.insert(WritingMode::UPRIGHT);
149 
150                             // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:
151                             //
152                             // > This value causes the used value of direction
153                             // > to be ltr, and for the purposes of bidi
154                             // > reordering, causes all characters to be treated
155                             // > as strong LTR.
156                             flags.remove(WritingMode::RTL);
157                             flags.remove(WritingMode::INLINE_REVERSED);
158                         },
159                         TextOrientation::Sideways => {
160                             flags.insert(WritingMode::TEXT_SIDEWAYS);
161                         },
162                     }
163                 },
164                 _ => {},
165             }
166         }
167 
168         flags
169     }
170 
171     #[inline]
is_vertical(&self) -> bool172     pub fn is_vertical(&self) -> bool {
173         self.intersects(WritingMode::VERTICAL)
174     }
175 
176     #[inline]
is_horizontal(&self) -> bool177     pub fn is_horizontal(&self) -> bool {
178         !self.is_vertical()
179     }
180 
181     /// Assuming .is_vertical(), does the block direction go left to right?
182     #[inline]
is_vertical_lr(&self) -> bool183     pub fn is_vertical_lr(&self) -> bool {
184         self.intersects(WritingMode::VERTICAL_LR)
185     }
186 
187     /// Assuming .is_vertical(), does the inline direction go top to bottom?
188     #[inline]
is_inline_tb(&self) -> bool189     pub fn is_inline_tb(&self) -> bool {
190         // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
191         !self.intersects(WritingMode::INLINE_REVERSED)
192     }
193 
194     #[inline]
is_bidi_ltr(&self) -> bool195     pub fn is_bidi_ltr(&self) -> bool {
196         !self.intersects(WritingMode::RTL)
197     }
198 
199     #[inline]
is_sideways(&self) -> bool200     pub fn is_sideways(&self) -> bool {
201         self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
202     }
203 
204     #[inline]
is_upright(&self) -> bool205     pub fn is_upright(&self) -> bool {
206         self.intersects(WritingMode::UPRIGHT)
207     }
208 
209     /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
210     ///
211     /// | Return  | line-left is… | line-right is… |
212     /// |---------|---------------|----------------|
213     /// | `true`  | inline-start  | inline-end     |
214     /// | `false` | inline-end    | inline-start   |
215     #[inline]
line_left_is_inline_start(&self) -> bool216     pub fn line_left_is_inline_start(&self) -> bool {
217         // https://drafts.csswg.org/css-writing-modes/#inline-start
218         // “For boxes with a used direction value of ltr, this means the line-left side.
219         //  For boxes with a used direction value of rtl, this means the line-right side.”
220         self.is_bidi_ltr()
221     }
222 
223     #[inline]
inline_start_physical_side(&self) -> PhysicalSide224     pub fn inline_start_physical_side(&self) -> PhysicalSide {
225         match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
226             (false, _, true) => PhysicalSide::Left,
227             (false, _, false) => PhysicalSide::Right,
228             (true, true, _) => PhysicalSide::Top,
229             (true, false, _) => PhysicalSide::Bottom,
230         }
231     }
232 
233     #[inline]
inline_end_physical_side(&self) -> PhysicalSide234     pub fn inline_end_physical_side(&self) -> PhysicalSide {
235         match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
236             (false, _, true) => PhysicalSide::Right,
237             (false, _, false) => PhysicalSide::Left,
238             (true, true, _) => PhysicalSide::Bottom,
239             (true, false, _) => PhysicalSide::Top,
240         }
241     }
242 
243     #[inline]
block_start_physical_side(&self) -> PhysicalSide244     pub fn block_start_physical_side(&self) -> PhysicalSide {
245         match (self.is_vertical(), self.is_vertical_lr()) {
246             (false, _) => PhysicalSide::Top,
247             (true, true) => PhysicalSide::Left,
248             (true, false) => PhysicalSide::Right,
249         }
250     }
251 
252     #[inline]
block_end_physical_side(&self) -> PhysicalSide253     pub fn block_end_physical_side(&self) -> PhysicalSide {
254         match (self.is_vertical(), self.is_vertical_lr()) {
255             (false, _) => PhysicalSide::Bottom,
256             (true, true) => PhysicalSide::Right,
257             (true, false) => PhysicalSide::Left,
258         }
259     }
260 
261     #[inline]
physical_sides_to_corner( block_side: PhysicalSide, inline_side: PhysicalSide, ) -> PhysicalCorner262     fn physical_sides_to_corner(
263         block_side: PhysicalSide,
264         inline_side: PhysicalSide,
265     ) -> PhysicalCorner {
266         match (block_side, inline_side) {
267             (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => {
268                 PhysicalCorner::TopLeft
269             },
270             (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => {
271                 PhysicalCorner::TopRight
272             },
273             (PhysicalSide::Bottom, PhysicalSide::Right) |
274             (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
275             (PhysicalSide::Bottom, PhysicalSide::Left) |
276             (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
277             _ => unreachable!("block and inline sides must be orthogonal"),
278         }
279     }
280 
281     #[inline]
start_start_physical_corner(&self) -> PhysicalCorner282     pub fn start_start_physical_corner(&self) -> PhysicalCorner {
283         WritingMode::physical_sides_to_corner(
284             self.block_start_physical_side(),
285             self.inline_start_physical_side(),
286         )
287     }
288 
289     #[inline]
start_end_physical_corner(&self) -> PhysicalCorner290     pub fn start_end_physical_corner(&self) -> PhysicalCorner {
291         WritingMode::physical_sides_to_corner(
292             self.block_start_physical_side(),
293             self.inline_end_physical_side(),
294         )
295     }
296 
297     #[inline]
end_start_physical_corner(&self) -> PhysicalCorner298     pub fn end_start_physical_corner(&self) -> PhysicalCorner {
299         WritingMode::physical_sides_to_corner(
300             self.block_end_physical_side(),
301             self.inline_start_physical_side(),
302         )
303     }
304 
305     #[inline]
end_end_physical_corner(&self) -> PhysicalCorner306     pub fn end_end_physical_corner(&self) -> PhysicalCorner {
307         WritingMode::physical_sides_to_corner(
308             self.block_end_physical_side(),
309             self.inline_end_physical_side(),
310         )
311     }
312 
313     #[inline]
block_flow_direction(&self) -> BlockFlowDirection314     pub fn block_flow_direction(&self) -> BlockFlowDirection {
315         match (self.is_vertical(), self.is_vertical_lr()) {
316             (false, _) => BlockFlowDirection::TopToBottom,
317             (true, true) => BlockFlowDirection::LeftToRight,
318             (true, false) => BlockFlowDirection::RightToLeft,
319         }
320     }
321 
322     #[inline]
inline_base_direction(&self) -> InlineBaseDirection323     pub fn inline_base_direction(&self) -> InlineBaseDirection {
324         if self.intersects(WritingMode::RTL) {
325             InlineBaseDirection::RightToLeft
326         } else {
327             InlineBaseDirection::LeftToRight
328         }
329     }
330 
331     #[inline]
332     /// The default bidirectional embedding level for this writing mode.
333     ///
334     /// Returns bidi level 0 if the mode is LTR, or 1 otherwise.
to_bidi_level(&self) -> bidi::Level335     pub fn to_bidi_level(&self) -> bidi::Level {
336         if self.is_bidi_ltr() {
337             bidi::Level::ltr()
338         } else {
339             bidi::Level::rtl()
340         }
341     }
342 }
343 
344 impl fmt::Display for WritingMode {
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>345     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
346         if self.is_vertical() {
347             write!(formatter, "V")?;
348             if self.is_vertical_lr() {
349                 write!(formatter, " LR")?;
350             } else {
351                 write!(formatter, " RL")?;
352             }
353             if self.is_sideways() {
354                 write!(formatter, " Sideways")?;
355             }
356             if self.intersects(WritingMode::LINE_INVERTED) {
357                 write!(formatter, " Inverted")?;
358             }
359         } else {
360             write!(formatter, "H")?;
361         }
362         if self.is_bidi_ltr() {
363             write!(formatter, " LTR")
364         } else {
365             write!(formatter, " RTL")
366         }
367     }
368 }
369 
370 /// Wherever logical geometry is used, the writing mode is known based on context:
371 /// every method takes a `mode` parameter.
372 /// However, this context is easy to get wrong.
373 /// In debug builds only, logical geometry objects store their writing mode
374 /// (in addition to taking it as a parameter to methods) and check it.
375 /// In non-debug builds, make this storage zero-size and the checks no-ops.
376 #[cfg(not(debug_assertions))]
377 #[derive(Clone, Copy, Eq, PartialEq)]
378 #[cfg_attr(feature = "servo", derive(Serialize))]
379 struct DebugWritingMode;
380 
381 #[cfg(debug_assertions)]
382 #[derive(Clone, Copy, Eq, PartialEq)]
383 #[cfg_attr(feature = "servo", derive(Serialize))]
384 struct DebugWritingMode {
385     mode: WritingMode,
386 }
387 
388 #[cfg(not(debug_assertions))]
389 impl DebugWritingMode {
390     #[inline]
check(&self, _other: WritingMode)391     fn check(&self, _other: WritingMode) {}
392 
393     #[inline]
check_debug(&self, _other: DebugWritingMode)394     fn check_debug(&self, _other: DebugWritingMode) {}
395 
396     #[inline]
new(_mode: WritingMode) -> DebugWritingMode397     fn new(_mode: WritingMode) -> DebugWritingMode {
398         DebugWritingMode
399     }
400 }
401 
402 #[cfg(debug_assertions)]
403 impl DebugWritingMode {
404     #[inline]
check(&self, other: WritingMode)405     fn check(&self, other: WritingMode) {
406         assert_eq!(self.mode, other)
407     }
408 
409     #[inline]
check_debug(&self, other: DebugWritingMode)410     fn check_debug(&self, other: DebugWritingMode) {
411         assert_eq!(self.mode, other.mode)
412     }
413 
414     #[inline]
new(mode: WritingMode) -> DebugWritingMode415     fn new(mode: WritingMode) -> DebugWritingMode {
416         DebugWritingMode { mode: mode }
417     }
418 }
419 
420 impl Debug for DebugWritingMode {
421     #[cfg(not(debug_assertions))]
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>422     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
423         write!(formatter, "?")
424     }
425 
426     #[cfg(debug_assertions)]
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>427     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
428         write!(formatter, "{}", self.mode)
429     }
430 }
431 
432 // Used to specify the logical direction.
433 #[derive(Clone, Copy, Debug, PartialEq)]
434 #[cfg_attr(feature = "servo", derive(Serialize))]
435 pub enum Direction {
436     Inline,
437     Block,
438 }
439 
440 /// A 2D size in flow-relative dimensions
441 #[derive(Clone, Copy, Eq, PartialEq)]
442 #[cfg_attr(feature = "servo", derive(Serialize))]
443 pub struct LogicalSize<T> {
444     pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
445     pub block: T,  // block-size, a.k.a. logical height, a.k.a. extent
446     debug_writing_mode: DebugWritingMode,
447 }
448 
449 impl<T: Debug> Debug for LogicalSize<T> {
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>450     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
451         write!(
452             formatter,
453             "LogicalSize({:?}, i{:?}×b{:?})",
454             self.debug_writing_mode, self.inline, self.block
455         )
456     }
457 }
458 
459 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
460 impl<T: Zero> LogicalSize<T> {
461     #[inline]
zero(mode: WritingMode) -> LogicalSize<T>462     pub fn zero(mode: WritingMode) -> LogicalSize<T> {
463         LogicalSize {
464             inline: Zero::zero(),
465             block: Zero::zero(),
466             debug_writing_mode: DebugWritingMode::new(mode),
467         }
468     }
469 }
470 
471 impl<T> LogicalSize<T> {
472     #[inline]
new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T>473     pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
474         LogicalSize {
475             inline: inline,
476             block: block,
477             debug_writing_mode: DebugWritingMode::new(mode),
478         }
479     }
480 
481     #[inline]
from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T>482     pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
483         if mode.is_vertical() {
484             LogicalSize::new(mode, size.height, size.width)
485         } else {
486             LogicalSize::new(mode, size.width, size.height)
487         }
488     }
489 }
490 
491 impl<T: Copy> LogicalSize<T> {
492     #[inline]
width(&self, mode: WritingMode) -> T493     pub fn width(&self, mode: WritingMode) -> T {
494         self.debug_writing_mode.check(mode);
495         if mode.is_vertical() {
496             self.block
497         } else {
498             self.inline
499         }
500     }
501 
502     #[inline]
set_width(&mut self, mode: WritingMode, width: T)503     pub fn set_width(&mut self, mode: WritingMode, width: T) {
504         self.debug_writing_mode.check(mode);
505         if mode.is_vertical() {
506             self.block = width
507         } else {
508             self.inline = width
509         }
510     }
511 
512     #[inline]
height(&self, mode: WritingMode) -> T513     pub fn height(&self, mode: WritingMode) -> T {
514         self.debug_writing_mode.check(mode);
515         if mode.is_vertical() {
516             self.inline
517         } else {
518             self.block
519         }
520     }
521 
522     #[inline]
set_height(&mut self, mode: WritingMode, height: T)523     pub fn set_height(&mut self, mode: WritingMode, height: T) {
524         self.debug_writing_mode.check(mode);
525         if mode.is_vertical() {
526             self.inline = height
527         } else {
528             self.block = height
529         }
530     }
531 
532     #[inline]
to_physical(&self, mode: WritingMode) -> Size2D<T>533     pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
534         self.debug_writing_mode.check(mode);
535         if mode.is_vertical() {
536             Size2D::new(self.block, self.inline)
537         } else {
538             Size2D::new(self.inline, self.block)
539         }
540     }
541 
542     #[inline]
convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T>543     pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
544         if mode_from == mode_to {
545             self.debug_writing_mode.check(mode_from);
546             *self
547         } else {
548             LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
549         }
550     }
551 }
552 
553 impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
554     type Output = LogicalSize<T>;
555 
556     #[inline]
add(self, other: LogicalSize<T>) -> LogicalSize<T>557     fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
558         self.debug_writing_mode
559             .check_debug(other.debug_writing_mode);
560         LogicalSize {
561             debug_writing_mode: self.debug_writing_mode,
562             inline: self.inline + other.inline,
563             block: self.block + other.block,
564         }
565     }
566 }
567 
568 impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
569     type Output = LogicalSize<T>;
570 
571     #[inline]
sub(self, other: LogicalSize<T>) -> LogicalSize<T>572     fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
573         self.debug_writing_mode
574             .check_debug(other.debug_writing_mode);
575         LogicalSize {
576             debug_writing_mode: self.debug_writing_mode,
577             inline: self.inline - other.inline,
578             block: self.block - other.block,
579         }
580     }
581 }
582 
583 /// A 2D point in flow-relative dimensions
584 #[derive(Clone, Copy, Eq, PartialEq)]
585 #[cfg_attr(feature = "servo", derive(Serialize))]
586 pub struct LogicalPoint<T> {
587     /// inline-axis coordinate
588     pub i: T,
589     /// block-axis coordinate
590     pub b: T,
591     debug_writing_mode: DebugWritingMode,
592 }
593 
594 impl<T: Debug> Debug for LogicalPoint<T> {
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>595     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
596         write!(
597             formatter,
598             "LogicalPoint({:?} (i{:?}, b{:?}))",
599             self.debug_writing_mode, self.i, self.b
600         )
601     }
602 }
603 
604 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
605 impl<T: Zero> LogicalPoint<T> {
606     #[inline]
zero(mode: WritingMode) -> LogicalPoint<T>607     pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
608         LogicalPoint {
609             i: Zero::zero(),
610             b: Zero::zero(),
611             debug_writing_mode: DebugWritingMode::new(mode),
612         }
613     }
614 }
615 
616 impl<T: Copy> LogicalPoint<T> {
617     #[inline]
new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T>618     pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
619         LogicalPoint {
620             i: i,
621             b: b,
622             debug_writing_mode: DebugWritingMode::new(mode),
623         }
624     }
625 }
626 
627 impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
628     #[inline]
from_physical( mode: WritingMode, point: Point2D<T>, container_size: Size2D<T>, ) -> LogicalPoint<T>629     pub fn from_physical(
630         mode: WritingMode,
631         point: Point2D<T>,
632         container_size: Size2D<T>,
633     ) -> LogicalPoint<T> {
634         if mode.is_vertical() {
635             LogicalPoint {
636                 i: if mode.is_inline_tb() {
637                     point.y
638                 } else {
639                     container_size.height - point.y
640                 },
641                 b: if mode.is_vertical_lr() {
642                     point.x
643                 } else {
644                     container_size.width - point.x
645                 },
646                 debug_writing_mode: DebugWritingMode::new(mode),
647             }
648         } else {
649             LogicalPoint {
650                 i: if mode.is_bidi_ltr() {
651                     point.x
652                 } else {
653                     container_size.width - point.x
654                 },
655                 b: point.y,
656                 debug_writing_mode: DebugWritingMode::new(mode),
657             }
658         }
659     }
660 
661     #[inline]
x(&self, mode: WritingMode, container_size: Size2D<T>) -> T662     pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
663         self.debug_writing_mode.check(mode);
664         if mode.is_vertical() {
665             if mode.is_vertical_lr() {
666                 self.b
667             } else {
668                 container_size.width - self.b
669             }
670         } else {
671             if mode.is_bidi_ltr() {
672                 self.i
673             } else {
674                 container_size.width - self.i
675             }
676         }
677     }
678 
679     #[inline]
set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>)680     pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
681         self.debug_writing_mode.check(mode);
682         if mode.is_vertical() {
683             self.b = if mode.is_vertical_lr() {
684                 x
685             } else {
686                 container_size.width - x
687             }
688         } else {
689             self.i = if mode.is_bidi_ltr() {
690                 x
691             } else {
692                 container_size.width - x
693             }
694         }
695     }
696 
697     #[inline]
y(&self, mode: WritingMode, container_size: Size2D<T>) -> T698     pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
699         self.debug_writing_mode.check(mode);
700         if mode.is_vertical() {
701             if mode.is_inline_tb() {
702                 self.i
703             } else {
704                 container_size.height - self.i
705             }
706         } else {
707             self.b
708         }
709     }
710 
711     #[inline]
set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>)712     pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
713         self.debug_writing_mode.check(mode);
714         if mode.is_vertical() {
715             self.i = if mode.is_inline_tb() {
716                 y
717             } else {
718                 container_size.height - y
719             }
720         } else {
721             self.b = y
722         }
723     }
724 
725     #[inline]
to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T>726     pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
727         self.debug_writing_mode.check(mode);
728         if mode.is_vertical() {
729             Point2D::new(
730                 if mode.is_vertical_lr() {
731                     self.b
732                 } else {
733                     container_size.width - self.b
734                 },
735                 if mode.is_inline_tb() {
736                     self.i
737                 } else {
738                     container_size.height - self.i
739                 },
740             )
741         } else {
742             Point2D::new(
743                 if mode.is_bidi_ltr() {
744                     self.i
745                 } else {
746                     container_size.width - self.i
747                 },
748                 self.b,
749             )
750         }
751     }
752 
753     #[inline]
convert( &self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>, ) -> LogicalPoint<T>754     pub fn convert(
755         &self,
756         mode_from: WritingMode,
757         mode_to: WritingMode,
758         container_size: Size2D<T>,
759     ) -> LogicalPoint<T> {
760         if mode_from == mode_to {
761             self.debug_writing_mode.check(mode_from);
762             *self
763         } else {
764             LogicalPoint::from_physical(
765                 mode_to,
766                 self.to_physical(mode_from, container_size),
767                 container_size,
768             )
769         }
770     }
771 }
772 
773 impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
774     /// This doesn’t really makes sense,
775     /// but happens when dealing with multiple origins.
776     #[inline]
add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T>777     pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
778         self.debug_writing_mode
779             .check_debug(other.debug_writing_mode);
780         LogicalPoint {
781             debug_writing_mode: self.debug_writing_mode,
782             i: self.i + other.i,
783             b: self.b + other.b,
784         }
785     }
786 }
787 
788 impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
789     type Output = LogicalPoint<T>;
790 
791     #[inline]
add(self, other: LogicalSize<T>) -> LogicalPoint<T>792     fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
793         self.debug_writing_mode
794             .check_debug(other.debug_writing_mode);
795         LogicalPoint {
796             debug_writing_mode: self.debug_writing_mode,
797             i: self.i + other.inline,
798             b: self.b + other.block,
799         }
800     }
801 }
802 
803 impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
804     type Output = LogicalPoint<T>;
805 
806     #[inline]
sub(self, other: LogicalSize<T>) -> LogicalPoint<T>807     fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
808         self.debug_writing_mode
809             .check_debug(other.debug_writing_mode);
810         LogicalPoint {
811             debug_writing_mode: self.debug_writing_mode,
812             i: self.i - other.inline,
813             b: self.b - other.block,
814         }
815     }
816 }
817 
818 /// A "margin" in flow-relative dimensions
819 /// Represents the four sides of the margins, borders, or padding of a CSS box,
820 /// or a combination of those.
821 /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
822 #[derive(Clone, Copy, Eq, PartialEq)]
823 #[cfg_attr(feature = "servo", derive(Serialize))]
824 pub struct LogicalMargin<T> {
825     pub block_start: T,
826     pub inline_end: T,
827     pub block_end: T,
828     pub inline_start: T,
829     debug_writing_mode: DebugWritingMode,
830 }
831 
832 impl<T: Debug> Debug for LogicalMargin<T> {
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>833     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
834         let writing_mode_string = if cfg!(debug_assertions) {
835             format!("{:?}, ", self.debug_writing_mode)
836         } else {
837             "".to_owned()
838         };
839 
840         write!(
841             formatter,
842             "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
843             writing_mode_string,
844             self.inline_start,
845             self.inline_end,
846             self.block_start,
847             self.block_end
848         )
849     }
850 }
851 
852 impl<T: Zero> LogicalMargin<T> {
853     #[inline]
zero(mode: WritingMode) -> LogicalMargin<T>854     pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
855         LogicalMargin {
856             block_start: Zero::zero(),
857             inline_end: Zero::zero(),
858             block_end: Zero::zero(),
859             inline_start: Zero::zero(),
860             debug_writing_mode: DebugWritingMode::new(mode),
861         }
862     }
863 }
864 
865 impl<T> LogicalMargin<T> {
866     #[inline]
new( mode: WritingMode, block_start: T, inline_end: T, block_end: T, inline_start: T, ) -> LogicalMargin<T>867     pub fn new(
868         mode: WritingMode,
869         block_start: T,
870         inline_end: T,
871         block_end: T,
872         inline_start: T,
873     ) -> LogicalMargin<T> {
874         LogicalMargin {
875             block_start: block_start,
876             inline_end: inline_end,
877             block_end: block_end,
878             inline_start: inline_start,
879             debug_writing_mode: DebugWritingMode::new(mode),
880         }
881     }
882 
883     #[inline]
from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T>884     pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
885         let block_start;
886         let inline_end;
887         let block_end;
888         let inline_start;
889         if mode.is_vertical() {
890             if mode.is_vertical_lr() {
891                 block_start = offsets.left;
892                 block_end = offsets.right;
893             } else {
894                 block_start = offsets.right;
895                 block_end = offsets.left;
896             }
897             if mode.is_inline_tb() {
898                 inline_start = offsets.top;
899                 inline_end = offsets.bottom;
900             } else {
901                 inline_start = offsets.bottom;
902                 inline_end = offsets.top;
903             }
904         } else {
905             block_start = offsets.top;
906             block_end = offsets.bottom;
907             if mode.is_bidi_ltr() {
908                 inline_start = offsets.left;
909                 inline_end = offsets.right;
910             } else {
911                 inline_start = offsets.right;
912                 inline_end = offsets.left;
913             }
914         }
915         LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
916     }
917 }
918 
919 impl<T: Copy> LogicalMargin<T> {
920     #[inline]
new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T>921     pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
922         LogicalMargin::new(mode, value, value, value, value)
923     }
924 
925     #[inline]
top(&self, mode: WritingMode) -> T926     pub fn top(&self, mode: WritingMode) -> T {
927         self.debug_writing_mode.check(mode);
928         if mode.is_vertical() {
929             if mode.is_inline_tb() {
930                 self.inline_start
931             } else {
932                 self.inline_end
933             }
934         } else {
935             self.block_start
936         }
937     }
938 
939     #[inline]
set_top(&mut self, mode: WritingMode, top: T)940     pub fn set_top(&mut self, mode: WritingMode, top: T) {
941         self.debug_writing_mode.check(mode);
942         if mode.is_vertical() {
943             if mode.is_inline_tb() {
944                 self.inline_start = top
945             } else {
946                 self.inline_end = top
947             }
948         } else {
949             self.block_start = top
950         }
951     }
952 
953     #[inline]
right(&self, mode: WritingMode) -> T954     pub fn right(&self, mode: WritingMode) -> T {
955         self.debug_writing_mode.check(mode);
956         if mode.is_vertical() {
957             if mode.is_vertical_lr() {
958                 self.block_end
959             } else {
960                 self.block_start
961             }
962         } else {
963             if mode.is_bidi_ltr() {
964                 self.inline_end
965             } else {
966                 self.inline_start
967             }
968         }
969     }
970 
971     #[inline]
set_right(&mut self, mode: WritingMode, right: T)972     pub fn set_right(&mut self, mode: WritingMode, right: T) {
973         self.debug_writing_mode.check(mode);
974         if mode.is_vertical() {
975             if mode.is_vertical_lr() {
976                 self.block_end = right
977             } else {
978                 self.block_start = right
979             }
980         } else {
981             if mode.is_bidi_ltr() {
982                 self.inline_end = right
983             } else {
984                 self.inline_start = right
985             }
986         }
987     }
988 
989     #[inline]
bottom(&self, mode: WritingMode) -> T990     pub fn bottom(&self, mode: WritingMode) -> T {
991         self.debug_writing_mode.check(mode);
992         if mode.is_vertical() {
993             if mode.is_inline_tb() {
994                 self.inline_end
995             } else {
996                 self.inline_start
997             }
998         } else {
999             self.block_end
1000         }
1001     }
1002 
1003     #[inline]
set_bottom(&mut self, mode: WritingMode, bottom: T)1004     pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1005         self.debug_writing_mode.check(mode);
1006         if mode.is_vertical() {
1007             if mode.is_inline_tb() {
1008                 self.inline_end = bottom
1009             } else {
1010                 self.inline_start = bottom
1011             }
1012         } else {
1013             self.block_end = bottom
1014         }
1015     }
1016 
1017     #[inline]
left(&self, mode: WritingMode) -> T1018     pub fn left(&self, mode: WritingMode) -> T {
1019         self.debug_writing_mode.check(mode);
1020         if mode.is_vertical() {
1021             if mode.is_vertical_lr() {
1022                 self.block_start
1023             } else {
1024                 self.block_end
1025             }
1026         } else {
1027             if mode.is_bidi_ltr() {
1028                 self.inline_start
1029             } else {
1030                 self.inline_end
1031             }
1032         }
1033     }
1034 
1035     #[inline]
set_left(&mut self, mode: WritingMode, left: T)1036     pub fn set_left(&mut self, mode: WritingMode, left: T) {
1037         self.debug_writing_mode.check(mode);
1038         if mode.is_vertical() {
1039             if mode.is_vertical_lr() {
1040                 self.block_start = left
1041             } else {
1042                 self.block_end = left
1043             }
1044         } else {
1045             if mode.is_bidi_ltr() {
1046                 self.inline_start = left
1047             } else {
1048                 self.inline_end = left
1049             }
1050         }
1051     }
1052 
1053     #[inline]
to_physical(&self, mode: WritingMode) -> SideOffsets2D<T>1054     pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1055         self.debug_writing_mode.check(mode);
1056         let top;
1057         let right;
1058         let bottom;
1059         let left;
1060         if mode.is_vertical() {
1061             if mode.is_vertical_lr() {
1062                 left = self.block_start;
1063                 right = self.block_end;
1064             } else {
1065                 right = self.block_start;
1066                 left = self.block_end;
1067             }
1068             if mode.is_inline_tb() {
1069                 top = self.inline_start;
1070                 bottom = self.inline_end;
1071             } else {
1072                 bottom = self.inline_start;
1073                 top = self.inline_end;
1074             }
1075         } else {
1076             top = self.block_start;
1077             bottom = self.block_end;
1078             if mode.is_bidi_ltr() {
1079                 left = self.inline_start;
1080                 right = self.inline_end;
1081             } else {
1082                 right = self.inline_start;
1083                 left = self.inline_end;
1084             }
1085         }
1086         SideOffsets2D::new(top, right, bottom, left)
1087     }
1088 
1089     #[inline]
convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T>1090     pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1091         if mode_from == mode_to {
1092             self.debug_writing_mode.check(mode_from);
1093             *self
1094         } else {
1095             LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1096         }
1097     }
1098 }
1099 
1100 impl<T: PartialEq + Zero> LogicalMargin<T> {
1101     #[inline]
is_zero(&self) -> bool1102     pub fn is_zero(&self) -> bool {
1103         self.block_start == Zero::zero() &&
1104             self.inline_end == Zero::zero() &&
1105             self.block_end == Zero::zero() &&
1106             self.inline_start == Zero::zero()
1107     }
1108 }
1109 
1110 impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1111     #[inline]
inline_start_end(&self) -> T1112     pub fn inline_start_end(&self) -> T {
1113         self.inline_start + self.inline_end
1114     }
1115 
1116     #[inline]
block_start_end(&self) -> T1117     pub fn block_start_end(&self) -> T {
1118         self.block_start + self.block_end
1119     }
1120 
1121     #[inline]
start_end(&self, direction: Direction) -> T1122     pub fn start_end(&self, direction: Direction) -> T {
1123         match direction {
1124             Direction::Inline => self.inline_start + self.inline_end,
1125             Direction::Block => self.block_start + self.block_end,
1126         }
1127     }
1128 
1129     #[inline]
top_bottom(&self, mode: WritingMode) -> T1130     pub fn top_bottom(&self, mode: WritingMode) -> T {
1131         self.debug_writing_mode.check(mode);
1132         if mode.is_vertical() {
1133             self.inline_start_end()
1134         } else {
1135             self.block_start_end()
1136         }
1137     }
1138 
1139     #[inline]
left_right(&self, mode: WritingMode) -> T1140     pub fn left_right(&self, mode: WritingMode) -> T {
1141         self.debug_writing_mode.check(mode);
1142         if mode.is_vertical() {
1143             self.block_start_end()
1144         } else {
1145             self.inline_start_end()
1146         }
1147     }
1148 }
1149 
1150 impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1151     type Output = LogicalMargin<T>;
1152 
1153     #[inline]
add(self, other: LogicalMargin<T>) -> LogicalMargin<T>1154     fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1155         self.debug_writing_mode
1156             .check_debug(other.debug_writing_mode);
1157         LogicalMargin {
1158             debug_writing_mode: self.debug_writing_mode,
1159             block_start: self.block_start + other.block_start,
1160             inline_end: self.inline_end + other.inline_end,
1161             block_end: self.block_end + other.block_end,
1162             inline_start: self.inline_start + other.inline_start,
1163         }
1164     }
1165 }
1166 
1167 impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1168     type Output = LogicalMargin<T>;
1169 
1170     #[inline]
sub(self, other: LogicalMargin<T>) -> LogicalMargin<T>1171     fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1172         self.debug_writing_mode
1173             .check_debug(other.debug_writing_mode);
1174         LogicalMargin {
1175             debug_writing_mode: self.debug_writing_mode,
1176             block_start: self.block_start - other.block_start,
1177             inline_end: self.inline_end - other.inline_end,
1178             block_end: self.block_end - other.block_end,
1179             inline_start: self.inline_start - other.inline_start,
1180         }
1181     }
1182 }
1183 
1184 /// A rectangle in flow-relative dimensions
1185 #[derive(Clone, Copy, Eq, PartialEq)]
1186 #[cfg_attr(feature = "servo", derive(Serialize))]
1187 pub struct LogicalRect<T> {
1188     pub start: LogicalPoint<T>,
1189     pub size: LogicalSize<T>,
1190     debug_writing_mode: DebugWritingMode,
1191 }
1192 
1193 impl<T: Debug> Debug for LogicalRect<T> {
fmt(&self, formatter: &mut Formatter) -> Result<(), Error>1194     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1195         let writing_mode_string = if cfg!(debug_assertions) {
1196             format!("{:?}, ", self.debug_writing_mode)
1197         } else {
1198             "".to_owned()
1199         };
1200 
1201         write!(
1202             formatter,
1203             "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1204             writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1205         )
1206     }
1207 }
1208 
1209 impl<T: Zero> LogicalRect<T> {
1210     #[inline]
zero(mode: WritingMode) -> LogicalRect<T>1211     pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1212         LogicalRect {
1213             start: LogicalPoint::zero(mode),
1214             size: LogicalSize::zero(mode),
1215             debug_writing_mode: DebugWritingMode::new(mode),
1216         }
1217     }
1218 }
1219 
1220 impl<T: Copy> LogicalRect<T> {
1221     #[inline]
new( mode: WritingMode, inline_start: T, block_start: T, inline: T, block: T, ) -> LogicalRect<T>1222     pub fn new(
1223         mode: WritingMode,
1224         inline_start: T,
1225         block_start: T,
1226         inline: T,
1227         block: T,
1228     ) -> LogicalRect<T> {
1229         LogicalRect {
1230             start: LogicalPoint::new(mode, inline_start, block_start),
1231             size: LogicalSize::new(mode, inline, block),
1232             debug_writing_mode: DebugWritingMode::new(mode),
1233         }
1234     }
1235 
1236     #[inline]
from_point_size( mode: WritingMode, start: LogicalPoint<T>, size: LogicalSize<T>, ) -> LogicalRect<T>1237     pub fn from_point_size(
1238         mode: WritingMode,
1239         start: LogicalPoint<T>,
1240         size: LogicalSize<T>,
1241     ) -> LogicalRect<T> {
1242         start.debug_writing_mode.check(mode);
1243         size.debug_writing_mode.check(mode);
1244         LogicalRect {
1245             start: start,
1246             size: size,
1247             debug_writing_mode: DebugWritingMode::new(mode),
1248         }
1249     }
1250 }
1251 
1252 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1253     #[inline]
from_physical( mode: WritingMode, rect: Rect<T>, container_size: Size2D<T>, ) -> LogicalRect<T>1254     pub fn from_physical(
1255         mode: WritingMode,
1256         rect: Rect<T>,
1257         container_size: Size2D<T>,
1258     ) -> LogicalRect<T> {
1259         let inline_start;
1260         let block_start;
1261         let inline;
1262         let block;
1263         if mode.is_vertical() {
1264             inline = rect.size.height;
1265             block = rect.size.width;
1266             if mode.is_vertical_lr() {
1267                 block_start = rect.origin.x;
1268             } else {
1269                 block_start = container_size.width - (rect.origin.x + rect.size.width);
1270             }
1271             if mode.is_inline_tb() {
1272                 inline_start = rect.origin.y;
1273             } else {
1274                 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1275             }
1276         } else {
1277             inline = rect.size.width;
1278             block = rect.size.height;
1279             block_start = rect.origin.y;
1280             if mode.is_bidi_ltr() {
1281                 inline_start = rect.origin.x;
1282             } else {
1283                 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1284             }
1285         }
1286         LogicalRect {
1287             start: LogicalPoint::new(mode, inline_start, block_start),
1288             size: LogicalSize::new(mode, inline, block),
1289             debug_writing_mode: DebugWritingMode::new(mode),
1290         }
1291     }
1292 
1293     #[inline]
inline_end(&self) -> T1294     pub fn inline_end(&self) -> T {
1295         self.start.i + self.size.inline
1296     }
1297 
1298     #[inline]
block_end(&self) -> T1299     pub fn block_end(&self) -> T {
1300         self.start.b + self.size.block
1301     }
1302 
1303     #[inline]
to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T>1304     pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1305         self.debug_writing_mode.check(mode);
1306         let x;
1307         let y;
1308         let width;
1309         let height;
1310         if mode.is_vertical() {
1311             width = self.size.block;
1312             height = self.size.inline;
1313             if mode.is_vertical_lr() {
1314                 x = self.start.b;
1315             } else {
1316                 x = container_size.width - self.block_end();
1317             }
1318             if mode.is_inline_tb() {
1319                 y = self.start.i;
1320             } else {
1321                 y = container_size.height - self.inline_end();
1322             }
1323         } else {
1324             width = self.size.inline;
1325             height = self.size.block;
1326             y = self.start.b;
1327             if mode.is_bidi_ltr() {
1328                 x = self.start.i;
1329             } else {
1330                 x = container_size.width - self.inline_end();
1331             }
1332         }
1333         Rect {
1334             origin: Point2D::new(x, y),
1335             size: Size2D::new(width, height),
1336         }
1337     }
1338 
1339     #[inline]
convert( &self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>, ) -> LogicalRect<T>1340     pub fn convert(
1341         &self,
1342         mode_from: WritingMode,
1343         mode_to: WritingMode,
1344         container_size: Size2D<T>,
1345     ) -> LogicalRect<T> {
1346         if mode_from == mode_to {
1347             self.debug_writing_mode.check(mode_from);
1348             *self
1349         } else {
1350             LogicalRect::from_physical(
1351                 mode_to,
1352                 self.to_physical(mode_from, container_size),
1353                 container_size,
1354             )
1355         }
1356     }
1357 
translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T>1358     pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1359         LogicalRect {
1360             start: self.start + offset,
1361             ..*self
1362         }
1363     }
1364 
translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T>1365     pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1366         LogicalRect {
1367             start: self.start +
1368                 LogicalSize {
1369                     inline: offset.i,
1370                     block: offset.b,
1371                     debug_writing_mode: offset.debug_writing_mode,
1372                 },
1373             size: self.size,
1374             debug_writing_mode: self.debug_writing_mode,
1375         }
1376     }
1377 }
1378 
1379 impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1380     #[inline]
union(&self, other: &LogicalRect<T>) -> LogicalRect<T>1381     pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1382         self.debug_writing_mode
1383             .check_debug(other.debug_writing_mode);
1384 
1385         let inline_start = min(self.start.i, other.start.i);
1386         let block_start = min(self.start.b, other.start.b);
1387         LogicalRect {
1388             start: LogicalPoint {
1389                 i: inline_start,
1390                 b: block_start,
1391                 debug_writing_mode: self.debug_writing_mode,
1392             },
1393             size: LogicalSize {
1394                 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1395                 block: max(self.block_end(), other.block_end()) - block_start,
1396                 debug_writing_mode: self.debug_writing_mode,
1397             },
1398             debug_writing_mode: self.debug_writing_mode,
1399         }
1400     }
1401 }
1402 
1403 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1404     type Output = LogicalRect<T>;
1405 
1406     #[inline]
add(self, other: LogicalMargin<T>) -> LogicalRect<T>1407     fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1408         self.debug_writing_mode
1409             .check_debug(other.debug_writing_mode);
1410         LogicalRect {
1411             start: LogicalPoint {
1412                 // Growing a rectangle on the start side means pushing its
1413                 // start point on the negative direction.
1414                 i: self.start.i - other.inline_start,
1415                 b: self.start.b - other.block_start,
1416                 debug_writing_mode: self.debug_writing_mode,
1417             },
1418             size: LogicalSize {
1419                 inline: self.size.inline + other.inline_start_end(),
1420                 block: self.size.block + other.block_start_end(),
1421                 debug_writing_mode: self.debug_writing_mode,
1422             },
1423             debug_writing_mode: self.debug_writing_mode,
1424         }
1425     }
1426 }
1427 
1428 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1429     type Output = LogicalRect<T>;
1430 
1431     #[inline]
sub(self, other: LogicalMargin<T>) -> LogicalRect<T>1432     fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1433         self.debug_writing_mode
1434             .check_debug(other.debug_writing_mode);
1435         LogicalRect {
1436             start: LogicalPoint {
1437                 // Shrinking a rectangle on the start side means pushing its
1438                 // start point on the positive direction.
1439                 i: self.start.i + other.inline_start,
1440                 b: self.start.b + other.block_start,
1441                 debug_writing_mode: self.debug_writing_mode,
1442             },
1443             size: LogicalSize {
1444                 inline: self.size.inline - other.inline_start_end(),
1445                 block: self.size.block - other.block_start_end(),
1446                 debug_writing_mode: self.debug_writing_mode,
1447             },
1448             debug_writing_mode: self.debug_writing_mode,
1449         }
1450     }
1451 }
1452 
1453 #[derive(Clone, Copy, Debug, PartialEq)]
1454 pub enum PhysicalSide {
1455     Top,
1456     Right,
1457     Bottom,
1458     Left,
1459 }
1460 
1461 #[derive(Clone, Copy, Debug, PartialEq)]
1462 pub enum PhysicalCorner {
1463     TopLeft,
1464     TopRight,
1465     BottomRight,
1466     BottomLeft,
1467 }
1468