1use crate::derives::*;
8use crate::properties::style_structs;
9use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
10use euclid::num::Zero;
11use std::cmp::{max, min};
12use std::fmt::{self, Debug, Error, Formatter};
13use std::ops::{Add, Sub};
14
15pub enum BlockFlowDirection {
16 TopToBottom,
17 RightToLeft,
18 LeftToRight,
19}
20
21pub enum InlineBaseDirection {
22 LeftToRight,
23 RightToLeft,
24}
25
26fn sideways_enabled(_: &crate::parser::ParserContext) -> bool {
27 crate::pref!("layout.css.writing-mode.sideways.enabled", gecko = true)
28}
29
30#[allow(missing_docs)]
34#[derive(
35 Clone,
36 Copy,
37 Debug,
38 Eq,
39 FromPrimitive,
40 MallocSizeOf,
41 Parse,
42 PartialEq,
43 SpecifiedValueInfo,
44 ToComputedValue,
45 ToCss,
46 ToResolvedValue,
47 ToShmem,
48 ToTyped,
49)]
50#[repr(u8)]
51pub enum WritingModeProperty {
52 #[parse(aliases = "lr,lr-tb,rl,rl-tb")]
53 HorizontalTb,
54 #[parse(aliases = "tb,tb-rl")]
55 VerticalRl,
56 VerticalLr,
57 #[parse(condition = "sideways_enabled")]
58 SidewaysRl,
59 #[parse(condition = "sideways_enabled")]
60 SidewaysLr,
61}
62
63#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)]
65#[repr(C)]
66pub struct WritingMode(u8);
67bitflags!(
68 impl WritingMode: u8 {
69 const VERTICAL = 1 << 0;
72 const INLINE_REVERSED = 1 << 1;
79 const VERTICAL_LR = 1 << 2;
84 const LINE_INVERTED = 1 << 3;
89 const RTL = 1 << 4;
91 const VERTICAL_SIDEWAYS = 1 << 5;
99 const TEXT_SIDEWAYS = 1 << 6;
107 const UPRIGHT = 1 << 7;
115 const WRITING_MODE_HORIZONTAL_TB = 0;
119 const WRITING_MODE_VERTICAL_RL = WritingMode::VERTICAL.bits();
121 const WRITING_MODE_VERTICAL_LR = WritingMode::VERTICAL.bits() |
123 WritingMode::VERTICAL_LR.bits() |
124 WritingMode::LINE_INVERTED.bits();
125 const WRITING_MODE_SIDEWAYS_RL = WritingMode::VERTICAL.bits() |
127 WritingMode::VERTICAL_SIDEWAYS.bits();
128 const WRITING_MODE_SIDEWAYS_LR = WritingMode::VERTICAL.bits() |
130 WritingMode::VERTICAL_LR.bits() |
131 WritingMode::VERTICAL_SIDEWAYS.bits();
132 }
133);
134
135impl WritingMode {
136 pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
138 use crate::properties::longhands::direction::computed_value::T as Direction;
139 use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
140
141 let mut flags = WritingMode::empty();
142
143 let direction = *inheritedbox_style.get_direction();
144 let writing_mode = *inheritedbox_style.get_writing_mode();
145
146 match direction {
147 Direction::Ltr => {},
148 Direction::Rtl => {
149 flags.insert(WritingMode::RTL);
150 },
151 }
152
153 match writing_mode {
154 WritingModeProperty::HorizontalTb => {
155 if direction == Direction::Rtl {
156 flags.insert(WritingMode::INLINE_REVERSED);
157 }
158 },
159 WritingModeProperty::VerticalRl => {
160 flags.insert(WritingMode::WRITING_MODE_VERTICAL_RL);
161 if direction == Direction::Rtl {
162 flags.insert(WritingMode::INLINE_REVERSED);
163 }
164 },
165 WritingModeProperty::VerticalLr => {
166 flags.insert(WritingMode::WRITING_MODE_VERTICAL_LR);
167 if direction == Direction::Rtl {
168 flags.insert(WritingMode::INLINE_REVERSED);
169 }
170 },
171 WritingModeProperty::SidewaysRl => {
172 flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_RL);
173 if direction == Direction::Rtl {
174 flags.insert(WritingMode::INLINE_REVERSED);
175 }
176 },
177 WritingModeProperty::SidewaysLr => {
178 flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_LR);
179 if direction == Direction::Ltr {
180 flags.insert(WritingMode::INLINE_REVERSED);
181 }
182 },
183 }
184
185 match writing_mode {
188 WritingModeProperty::VerticalRl | WritingModeProperty::VerticalLr => {
189 match *inheritedbox_style.get_text_orientation() {
190 TextOrientation::Mixed => {},
191 TextOrientation::Upright => {
192 flags.insert(WritingMode::UPRIGHT);
193
194 flags.remove(WritingMode::RTL);
201 flags.remove(WritingMode::INLINE_REVERSED);
202 },
203 TextOrientation::Sideways => {
204 flags.insert(WritingMode::TEXT_SIDEWAYS);
205 },
206 }
207 },
208 _ => {},
209 }
210
211 flags
212 }
213
214 pub fn horizontal_tb() -> Self {
216 Self::empty()
217 }
218
219 #[inline]
220 pub fn is_vertical(&self) -> bool {
221 self.intersects(WritingMode::VERTICAL)
222 }
223
224 #[inline]
225 pub fn is_vertical_rl(&self) -> bool {
226 self.is_vertical() && !self.is_vertical_lr()
227 }
228
229 #[inline]
230 pub fn is_horizontal(&self) -> bool {
231 !self.is_vertical()
232 }
233
234 #[inline]
236 pub fn is_vertical_lr(&self) -> bool {
237 self.intersects(WritingMode::VERTICAL_LR)
238 }
239
240 #[inline]
242 pub fn is_inline_tb(&self) -> bool {
243 !self.intersects(WritingMode::INLINE_REVERSED)
245 }
246
247 #[inline]
248 pub fn is_bidi_ltr(&self) -> bool {
249 !self.intersects(WritingMode::RTL)
250 }
251
252 #[inline]
253 pub fn is_sideways(&self) -> bool {
254 self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
255 }
256
257 #[inline]
258 pub fn is_upright(&self) -> bool {
259 self.intersects(WritingMode::UPRIGHT)
260 }
261
262 #[inline]
269 pub fn line_left_is_inline_start(&self) -> bool {
270 self.is_bidi_ltr()
274 }
275
276 #[inline]
277 pub fn inline_start_physical_side(&self) -> PhysicalSide {
278 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
279 (false, _, true) => PhysicalSide::Left,
280 (false, _, false) => PhysicalSide::Right,
281 (true, true, _) => PhysicalSide::Top,
282 (true, false, _) => PhysicalSide::Bottom,
283 }
284 }
285
286 #[inline]
287 pub fn inline_end_physical_side(&self) -> PhysicalSide {
288 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
289 (false, _, true) => PhysicalSide::Right,
290 (false, _, false) => PhysicalSide::Left,
291 (true, true, _) => PhysicalSide::Bottom,
292 (true, false, _) => PhysicalSide::Top,
293 }
294 }
295
296 #[inline]
297 pub fn block_start_physical_side(&self) -> PhysicalSide {
298 match (self.is_vertical(), self.is_vertical_lr()) {
299 (false, _) => PhysicalSide::Top,
300 (true, true) => PhysicalSide::Left,
301 (true, false) => PhysicalSide::Right,
302 }
303 }
304
305 #[inline]
306 pub fn block_end_physical_side(&self) -> PhysicalSide {
307 match (self.is_vertical(), self.is_vertical_lr()) {
308 (false, _) => PhysicalSide::Bottom,
309 (true, true) => PhysicalSide::Right,
310 (true, false) => PhysicalSide::Left,
311 }
312 }
313
314 #[inline]
317 pub fn flipped_start_side(&self, side: PhysicalSide) -> PhysicalSide {
318 let bs = self.block_start_physical_side();
319 if side == bs {
320 return self.inline_start_physical_side();
321 }
322 let be = self.block_end_physical_side();
323 if side == be {
324 return self.inline_end_physical_side();
325 }
326 if side == self.inline_start_physical_side() {
327 return bs;
328 }
329 debug_assert_eq!(side, self.inline_end_physical_side());
330 be
331 }
332
333 #[inline]
334 pub fn start_start_physical_corner(&self) -> PhysicalCorner {
335 PhysicalCorner::from_sides(
336 self.block_start_physical_side(),
337 self.inline_start_physical_side(),
338 )
339 }
340
341 #[inline]
342 pub fn start_end_physical_corner(&self) -> PhysicalCorner {
343 PhysicalCorner::from_sides(
344 self.block_start_physical_side(),
345 self.inline_end_physical_side(),
346 )
347 }
348
349 #[inline]
350 pub fn end_start_physical_corner(&self) -> PhysicalCorner {
351 PhysicalCorner::from_sides(
352 self.block_end_physical_side(),
353 self.inline_start_physical_side(),
354 )
355 }
356
357 #[inline]
358 pub fn end_end_physical_corner(&self) -> PhysicalCorner {
359 PhysicalCorner::from_sides(
360 self.block_end_physical_side(),
361 self.inline_end_physical_side(),
362 )
363 }
364
365 #[inline]
366 pub fn block_flow_direction(&self) -> BlockFlowDirection {
367 match (self.is_vertical(), self.is_vertical_lr()) {
368 (false, _) => BlockFlowDirection::TopToBottom,
369 (true, true) => BlockFlowDirection::LeftToRight,
370 (true, false) => BlockFlowDirection::RightToLeft,
371 }
372 }
373
374 #[inline]
375 pub fn inline_base_direction(&self) -> InlineBaseDirection {
376 if self.intersects(WritingMode::RTL) {
377 InlineBaseDirection::RightToLeft
378 } else {
379 InlineBaseDirection::LeftToRight
380 }
381 }
382
383 #[inline]
384 pub fn is_text_vertical(&self) -> bool {
386 self.is_vertical() && !self.is_sideways()
387 }
388}
389
390impl fmt::Display for WritingMode {
391 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
392 if self.is_vertical() {
393 write!(formatter, "V")?;
394 if self.is_vertical_lr() {
395 write!(formatter, " LR")?;
396 } else {
397 write!(formatter, " RL")?;
398 }
399 if self.is_sideways() {
400 write!(formatter, " Sideways")?;
401 }
402 if self.intersects(WritingMode::LINE_INVERTED) {
403 write!(formatter, " Inverted")?;
404 }
405 } else {
406 write!(formatter, "H")?;
407 }
408 if self.is_bidi_ltr() {
409 write!(formatter, " LTR")
410 } else {
411 write!(formatter, " RTL")
412 }
413 }
414}
415
416#[cfg(not(debug_assertions))]
423#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
424struct DebugWritingMode;
425
426#[cfg(debug_assertions)]
427#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
428struct DebugWritingMode {
429 mode: WritingMode,
430}
431
432#[cfg(not(debug_assertions))]
433impl DebugWritingMode {
434 #[inline]
435 fn check(&self, _other: WritingMode) {}
436
437 #[inline]
438 fn check_debug(&self, _other: DebugWritingMode) {}
439
440 #[inline]
441 fn new(_mode: WritingMode) -> DebugWritingMode {
442 DebugWritingMode
443 }
444}
445
446#[cfg(debug_assertions)]
447impl DebugWritingMode {
448 #[inline]
449 fn check(&self, other: WritingMode) {
450 assert_eq!(self.mode, other)
451 }
452
453 #[inline]
454 fn check_debug(&self, other: DebugWritingMode) {
455 assert_eq!(self.mode, other.mode)
456 }
457
458 #[inline]
459 fn new(mode: WritingMode) -> DebugWritingMode {
460 DebugWritingMode { mode }
461 }
462}
463
464impl Debug for DebugWritingMode {
465 #[cfg(not(debug_assertions))]
466 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
467 write!(formatter, "?")
468 }
469
470 #[cfg(debug_assertions)]
471 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
472 write!(formatter, "{}", self.mode)
473 }
474}
475
476#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
478pub enum Direction {
479 Inline,
480 Block,
481}
482
483#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
485pub struct LogicalSize<T> {
486 pub inline: T, pub block: T, debug_writing_mode: DebugWritingMode,
489}
490
491impl<T: Debug> Debug for LogicalSize<T> {
492 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
493 write!(
494 formatter,
495 "LogicalSize({:?}, i{:?}×b{:?})",
496 self.debug_writing_mode, self.inline, self.block
497 )
498 }
499}
500
501impl<T: Zero> LogicalSize<T> {
503 #[inline]
504 pub fn zero(mode: WritingMode) -> LogicalSize<T> {
505 LogicalSize {
506 inline: Zero::zero(),
507 block: Zero::zero(),
508 debug_writing_mode: DebugWritingMode::new(mode),
509 }
510 }
511}
512
513impl<T> LogicalSize<T> {
514 #[inline]
515 pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
516 LogicalSize {
517 inline,
518 block,
519 debug_writing_mode: DebugWritingMode::new(mode),
520 }
521 }
522
523 #[inline]
524 pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
525 if mode.is_vertical() {
526 LogicalSize::new(mode, size.height, size.width)
527 } else {
528 LogicalSize::new(mode, size.width, size.height)
529 }
530 }
531}
532
533impl<T: Clone> LogicalSize<T> {
534 #[inline]
535 pub fn width(&self, mode: WritingMode) -> T {
536 self.debug_writing_mode.check(mode);
537 if mode.is_vertical() {
538 self.block.clone()
539 } else {
540 self.inline.clone()
541 }
542 }
543
544 #[inline]
545 pub fn set_width(&mut self, mode: WritingMode, width: T) {
546 self.debug_writing_mode.check(mode);
547 if mode.is_vertical() {
548 self.block = width
549 } else {
550 self.inline = width
551 }
552 }
553
554 #[inline]
555 pub fn height(&self, mode: WritingMode) -> T {
556 self.debug_writing_mode.check(mode);
557 if mode.is_vertical() {
558 self.inline.clone()
559 } else {
560 self.block.clone()
561 }
562 }
563
564 #[inline]
565 pub fn set_height(&mut self, mode: WritingMode, height: T) {
566 self.debug_writing_mode.check(mode);
567 if mode.is_vertical() {
568 self.inline = height
569 } else {
570 self.block = height
571 }
572 }
573
574 #[inline]
575 pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
576 self.debug_writing_mode.check(mode);
577 if mode.is_vertical() {
578 Size2D::new(self.block.clone(), self.inline.clone())
579 } else {
580 Size2D::new(self.inline.clone(), self.block.clone())
581 }
582 }
583
584 #[inline]
585 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
586 if mode_from == mode_to {
587 self.debug_writing_mode.check(mode_from);
588 self.clone()
589 } else {
590 LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
591 }
592 }
593}
594
595impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
596 type Output = LogicalSize<T>;
597
598 #[inline]
599 fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
600 self.debug_writing_mode
601 .check_debug(other.debug_writing_mode);
602 LogicalSize {
603 debug_writing_mode: self.debug_writing_mode,
604 inline: self.inline + other.inline,
605 block: self.block + other.block,
606 }
607 }
608}
609
610impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
611 type Output = LogicalSize<T>;
612
613 #[inline]
614 fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
615 self.debug_writing_mode
616 .check_debug(other.debug_writing_mode);
617 LogicalSize {
618 debug_writing_mode: self.debug_writing_mode,
619 inline: self.inline - other.inline,
620 block: self.block - other.block,
621 }
622 }
623}
624
625#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
627pub struct LogicalPoint<T> {
628 pub i: T,
630 pub b: T,
632 debug_writing_mode: DebugWritingMode,
633}
634
635impl<T: Debug> Debug for LogicalPoint<T> {
636 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
637 write!(
638 formatter,
639 "LogicalPoint({:?} (i{:?}, b{:?}))",
640 self.debug_writing_mode, self.i, self.b
641 )
642 }
643}
644
645impl<T: Zero> LogicalPoint<T> {
647 #[inline]
648 pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
649 LogicalPoint {
650 i: Zero::zero(),
651 b: Zero::zero(),
652 debug_writing_mode: DebugWritingMode::new(mode),
653 }
654 }
655}
656
657impl<T: Copy> LogicalPoint<T> {
658 #[inline]
659 pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
660 LogicalPoint {
661 i,
662 b,
663 debug_writing_mode: DebugWritingMode::new(mode),
664 }
665 }
666}
667
668impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
669 #[inline]
670 pub fn from_physical(
671 mode: WritingMode,
672 point: Point2D<T>,
673 container_size: Size2D<T>,
674 ) -> LogicalPoint<T> {
675 if mode.is_vertical() {
676 LogicalPoint {
677 i: if mode.is_inline_tb() {
678 point.y
679 } else {
680 container_size.height - point.y
681 },
682 b: if mode.is_vertical_lr() {
683 point.x
684 } else {
685 container_size.width - point.x
686 },
687 debug_writing_mode: DebugWritingMode::new(mode),
688 }
689 } else {
690 LogicalPoint {
691 i: if mode.is_bidi_ltr() {
692 point.x
693 } else {
694 container_size.width - point.x
695 },
696 b: point.y,
697 debug_writing_mode: DebugWritingMode::new(mode),
698 }
699 }
700 }
701
702 #[inline]
703 pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
704 self.debug_writing_mode.check(mode);
705 if mode.is_vertical() {
706 if mode.is_vertical_lr() {
707 self.b
708 } else {
709 container_size.width - self.b
710 }
711 } else if mode.is_bidi_ltr() {
712 self.i
713 } else {
714 container_size.width - self.i
715 }
716 }
717
718 #[inline]
719 pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
720 self.debug_writing_mode.check(mode);
721 if mode.is_vertical() {
722 self.b = if mode.is_vertical_lr() {
723 x
724 } else {
725 container_size.width - x
726 }
727 } else {
728 self.i = if mode.is_bidi_ltr() {
729 x
730 } else {
731 container_size.width - x
732 }
733 }
734 }
735
736 #[inline]
737 pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
738 self.debug_writing_mode.check(mode);
739 if mode.is_vertical() {
740 if mode.is_inline_tb() {
741 self.i
742 } else {
743 container_size.height - self.i
744 }
745 } else {
746 self.b
747 }
748 }
749
750 #[inline]
751 pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
752 self.debug_writing_mode.check(mode);
753 if mode.is_vertical() {
754 self.i = if mode.is_inline_tb() {
755 y
756 } else {
757 container_size.height - y
758 }
759 } else {
760 self.b = y
761 }
762 }
763
764 #[inline]
765 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
766 self.debug_writing_mode.check(mode);
767 if mode.is_vertical() {
768 Point2D::new(
769 if mode.is_vertical_lr() {
770 self.b
771 } else {
772 container_size.width - self.b
773 },
774 if mode.is_inline_tb() {
775 self.i
776 } else {
777 container_size.height - self.i
778 },
779 )
780 } else {
781 Point2D::new(
782 if mode.is_bidi_ltr() {
783 self.i
784 } else {
785 container_size.width - self.i
786 },
787 self.b,
788 )
789 }
790 }
791
792 #[inline]
793 pub fn convert(
794 &self,
795 mode_from: WritingMode,
796 mode_to: WritingMode,
797 container_size: Size2D<T>,
798 ) -> LogicalPoint<T> {
799 if mode_from == mode_to {
800 self.debug_writing_mode.check(mode_from);
801 *self
802 } else {
803 LogicalPoint::from_physical(
804 mode_to,
805 self.to_physical(mode_from, container_size),
806 container_size,
807 )
808 }
809 }
810}
811
812impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
813 #[inline]
816 pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
817 self.debug_writing_mode
818 .check_debug(other.debug_writing_mode);
819 LogicalPoint {
820 debug_writing_mode: self.debug_writing_mode,
821 i: self.i + other.i,
822 b: self.b + other.b,
823 }
824 }
825}
826
827impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
828 type Output = LogicalPoint<T>;
829
830 #[inline]
831 fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
832 self.debug_writing_mode
833 .check_debug(other.debug_writing_mode);
834 LogicalPoint {
835 debug_writing_mode: self.debug_writing_mode,
836 i: self.i + other.inline,
837 b: self.b + other.block,
838 }
839 }
840}
841
842impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
843 type Output = LogicalPoint<T>;
844
845 #[inline]
846 fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
847 self.debug_writing_mode
848 .check_debug(other.debug_writing_mode);
849 LogicalPoint {
850 debug_writing_mode: self.debug_writing_mode,
851 i: self.i - other.inline,
852 b: self.b - other.block,
853 }
854 }
855}
856
857#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
862pub struct LogicalMargin<T> {
863 pub block_start: T,
864 pub inline_end: T,
865 pub block_end: T,
866 pub inline_start: T,
867 debug_writing_mode: DebugWritingMode,
868}
869
870impl<T: Debug> Debug for LogicalMargin<T> {
871 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
872 let writing_mode_string = if cfg!(debug_assertions) {
873 format!("{:?}, ", self.debug_writing_mode)
874 } else {
875 "".to_owned()
876 };
877
878 write!(
879 formatter,
880 "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
881 writing_mode_string,
882 self.inline_start,
883 self.inline_end,
884 self.block_start,
885 self.block_end
886 )
887 }
888}
889
890impl<T: Zero> LogicalMargin<T> {
891 #[inline]
892 pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
893 LogicalMargin {
894 block_start: Zero::zero(),
895 inline_end: Zero::zero(),
896 block_end: Zero::zero(),
897 inline_start: Zero::zero(),
898 debug_writing_mode: DebugWritingMode::new(mode),
899 }
900 }
901}
902
903impl<T> LogicalMargin<T> {
904 #[inline]
905 pub fn new(
906 mode: WritingMode,
907 block_start: T,
908 inline_end: T,
909 block_end: T,
910 inline_start: T,
911 ) -> LogicalMargin<T> {
912 LogicalMargin {
913 block_start,
914 inline_end,
915 block_end,
916 inline_start,
917 debug_writing_mode: DebugWritingMode::new(mode),
918 }
919 }
920
921 #[inline]
922 pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
923 let block_start;
924 let inline_end;
925 let block_end;
926 let inline_start;
927 if mode.is_vertical() {
928 if mode.is_vertical_lr() {
929 block_start = offsets.left;
930 block_end = offsets.right;
931 } else {
932 block_start = offsets.right;
933 block_end = offsets.left;
934 }
935 if mode.is_inline_tb() {
936 inline_start = offsets.top;
937 inline_end = offsets.bottom;
938 } else {
939 inline_start = offsets.bottom;
940 inline_end = offsets.top;
941 }
942 } else {
943 block_start = offsets.top;
944 block_end = offsets.bottom;
945 if mode.is_bidi_ltr() {
946 inline_start = offsets.left;
947 inline_end = offsets.right;
948 } else {
949 inline_start = offsets.right;
950 inline_end = offsets.left;
951 }
952 }
953 LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
954 }
955}
956
957impl<T: Clone> LogicalMargin<T> {
958 #[inline]
959 pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
960 LogicalMargin::new(mode, value.clone(), value.clone(), value.clone(), value)
961 }
962
963 #[inline]
964 pub fn top(&self, mode: WritingMode) -> T {
965 self.debug_writing_mode.check(mode);
966 if mode.is_vertical() {
967 if mode.is_inline_tb() {
968 self.inline_start.clone()
969 } else {
970 self.inline_end.clone()
971 }
972 } else {
973 self.block_start.clone()
974 }
975 }
976
977 #[inline]
978 pub fn set_top(&mut self, mode: WritingMode, top: T) {
979 self.debug_writing_mode.check(mode);
980 if mode.is_vertical() {
981 if mode.is_inline_tb() {
982 self.inline_start = top
983 } else {
984 self.inline_end = top
985 }
986 } else {
987 self.block_start = top
988 }
989 }
990
991 #[inline]
992 pub fn right(&self, mode: WritingMode) -> T {
993 self.debug_writing_mode.check(mode);
994 if mode.is_vertical() {
995 if mode.is_vertical_lr() {
996 self.block_end.clone()
997 } else {
998 self.block_start.clone()
999 }
1000 } else if mode.is_bidi_ltr() {
1001 self.inline_end.clone()
1002 } else {
1003 self.inline_start.clone()
1004 }
1005 }
1006
1007 #[inline]
1008 pub fn set_right(&mut self, mode: WritingMode, right: T) {
1009 self.debug_writing_mode.check(mode);
1010 if mode.is_vertical() {
1011 if mode.is_vertical_lr() {
1012 self.block_end = right
1013 } else {
1014 self.block_start = right
1015 }
1016 } else if mode.is_bidi_ltr() {
1017 self.inline_end = right
1018 } else {
1019 self.inline_start = right
1020 }
1021 }
1022
1023 #[inline]
1024 pub fn bottom(&self, mode: WritingMode) -> T {
1025 self.debug_writing_mode.check(mode);
1026 if mode.is_vertical() {
1027 if mode.is_inline_tb() {
1028 self.inline_end.clone()
1029 } else {
1030 self.inline_start.clone()
1031 }
1032 } else {
1033 self.block_end.clone()
1034 }
1035 }
1036
1037 #[inline]
1038 pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1039 self.debug_writing_mode.check(mode);
1040 if mode.is_vertical() {
1041 if mode.is_inline_tb() {
1042 self.inline_end = bottom
1043 } else {
1044 self.inline_start = bottom
1045 }
1046 } else {
1047 self.block_end = bottom
1048 }
1049 }
1050
1051 #[inline]
1052 pub fn left(&self, mode: WritingMode) -> T {
1053 self.debug_writing_mode.check(mode);
1054 if mode.is_vertical() {
1055 if mode.is_vertical_lr() {
1056 self.block_start.clone()
1057 } else {
1058 self.block_end.clone()
1059 }
1060 } else if mode.is_bidi_ltr() {
1061 self.inline_start.clone()
1062 } else {
1063 self.inline_end.clone()
1064 }
1065 }
1066
1067 #[inline]
1068 pub fn set_left(&mut self, mode: WritingMode, left: T) {
1069 self.debug_writing_mode.check(mode);
1070 if mode.is_vertical() {
1071 if mode.is_vertical_lr() {
1072 self.block_start = left
1073 } else {
1074 self.block_end = left
1075 }
1076 } else if mode.is_bidi_ltr() {
1077 self.inline_start = left
1078 } else {
1079 self.inline_end = left
1080 }
1081 }
1082
1083 #[inline]
1084 pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1085 self.debug_writing_mode.check(mode);
1086 let top;
1087 let right;
1088 let bottom;
1089 let left;
1090 if mode.is_vertical() {
1091 if mode.is_vertical_lr() {
1092 left = self.block_start.clone();
1093 right = self.block_end.clone();
1094 } else {
1095 right = self.block_start.clone();
1096 left = self.block_end.clone();
1097 }
1098 if mode.is_inline_tb() {
1099 top = self.inline_start.clone();
1100 bottom = self.inline_end.clone();
1101 } else {
1102 bottom = self.inline_start.clone();
1103 top = self.inline_end.clone();
1104 }
1105 } else {
1106 top = self.block_start.clone();
1107 bottom = self.block_end.clone();
1108 if mode.is_bidi_ltr() {
1109 left = self.inline_start.clone();
1110 right = self.inline_end.clone();
1111 } else {
1112 right = self.inline_start.clone();
1113 left = self.inline_end.clone();
1114 }
1115 }
1116 SideOffsets2D::new(top, right, bottom, left)
1117 }
1118
1119 #[inline]
1120 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1121 if mode_from == mode_to {
1122 self.debug_writing_mode.check(mode_from);
1123 self.clone()
1124 } else {
1125 LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1126 }
1127 }
1128}
1129
1130impl<T: PartialEq + Zero> LogicalMargin<T> {
1131 #[inline]
1132 pub fn is_zero(&self) -> bool {
1133 self.block_start == Zero::zero()
1134 && self.inline_end == Zero::zero()
1135 && self.block_end == Zero::zero()
1136 && self.inline_start == Zero::zero()
1137 }
1138}
1139
1140impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1141 #[inline]
1142 pub fn inline_start_end(&self) -> T {
1143 self.inline_start + self.inline_end
1144 }
1145
1146 #[inline]
1147 pub fn block_start_end(&self) -> T {
1148 self.block_start + self.block_end
1149 }
1150
1151 #[inline]
1152 pub fn start_end(&self, direction: Direction) -> T {
1153 match direction {
1154 Direction::Inline => self.inline_start + self.inline_end,
1155 Direction::Block => self.block_start + self.block_end,
1156 }
1157 }
1158
1159 #[inline]
1160 pub fn top_bottom(&self, mode: WritingMode) -> T {
1161 self.debug_writing_mode.check(mode);
1162 if mode.is_vertical() {
1163 self.inline_start_end()
1164 } else {
1165 self.block_start_end()
1166 }
1167 }
1168
1169 #[inline]
1170 pub fn left_right(&self, mode: WritingMode) -> T {
1171 self.debug_writing_mode.check(mode);
1172 if mode.is_vertical() {
1173 self.block_start_end()
1174 } else {
1175 self.inline_start_end()
1176 }
1177 }
1178}
1179
1180impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1181 type Output = LogicalMargin<T>;
1182
1183 #[inline]
1184 fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1185 self.debug_writing_mode
1186 .check_debug(other.debug_writing_mode);
1187 LogicalMargin {
1188 debug_writing_mode: self.debug_writing_mode,
1189 block_start: self.block_start + other.block_start,
1190 inline_end: self.inline_end + other.inline_end,
1191 block_end: self.block_end + other.block_end,
1192 inline_start: self.inline_start + other.inline_start,
1193 }
1194 }
1195}
1196
1197impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1198 type Output = LogicalMargin<T>;
1199
1200 #[inline]
1201 fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1202 self.debug_writing_mode
1203 .check_debug(other.debug_writing_mode);
1204 LogicalMargin {
1205 debug_writing_mode: self.debug_writing_mode,
1206 block_start: self.block_start - other.block_start,
1207 inline_end: self.inline_end - other.inline_end,
1208 block_end: self.block_end - other.block_end,
1209 inline_start: self.inline_start - other.inline_start,
1210 }
1211 }
1212}
1213
1214#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
1216pub struct LogicalRect<T> {
1217 pub start: LogicalPoint<T>,
1218 pub size: LogicalSize<T>,
1219 debug_writing_mode: DebugWritingMode,
1220}
1221
1222impl<T: Debug> Debug for LogicalRect<T> {
1223 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1224 let writing_mode_string = if cfg!(debug_assertions) {
1225 format!("{:?}, ", self.debug_writing_mode)
1226 } else {
1227 "".to_owned()
1228 };
1229
1230 write!(
1231 formatter,
1232 "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1233 writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1234 )
1235 }
1236}
1237
1238impl<T: Zero> LogicalRect<T> {
1239 #[inline]
1240 pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1241 LogicalRect {
1242 start: LogicalPoint::zero(mode),
1243 size: LogicalSize::zero(mode),
1244 debug_writing_mode: DebugWritingMode::new(mode),
1245 }
1246 }
1247}
1248
1249impl<T: Copy> LogicalRect<T> {
1250 #[inline]
1251 pub fn new(
1252 mode: WritingMode,
1253 inline_start: T,
1254 block_start: T,
1255 inline: T,
1256 block: T,
1257 ) -> LogicalRect<T> {
1258 LogicalRect {
1259 start: LogicalPoint::new(mode, inline_start, block_start),
1260 size: LogicalSize::new(mode, inline, block),
1261 debug_writing_mode: DebugWritingMode::new(mode),
1262 }
1263 }
1264
1265 #[inline]
1266 pub fn from_point_size(
1267 mode: WritingMode,
1268 start: LogicalPoint<T>,
1269 size: LogicalSize<T>,
1270 ) -> LogicalRect<T> {
1271 start.debug_writing_mode.check(mode);
1272 size.debug_writing_mode.check(mode);
1273 LogicalRect {
1274 start,
1275 size,
1276 debug_writing_mode: DebugWritingMode::new(mode),
1277 }
1278 }
1279}
1280
1281impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1282 #[inline]
1283 pub fn from_physical(
1284 mode: WritingMode,
1285 rect: Rect<T>,
1286 container_size: Size2D<T>,
1287 ) -> LogicalRect<T> {
1288 let inline_start;
1289 let block_start;
1290 let inline;
1291 let block;
1292 if mode.is_vertical() {
1293 inline = rect.size.height;
1294 block = rect.size.width;
1295 if mode.is_vertical_lr() {
1296 block_start = rect.origin.x;
1297 } else {
1298 block_start = container_size.width - (rect.origin.x + rect.size.width);
1299 }
1300 if mode.is_inline_tb() {
1301 inline_start = rect.origin.y;
1302 } else {
1303 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1304 }
1305 } else {
1306 inline = rect.size.width;
1307 block = rect.size.height;
1308 block_start = rect.origin.y;
1309 if mode.is_bidi_ltr() {
1310 inline_start = rect.origin.x;
1311 } else {
1312 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1313 }
1314 }
1315 LogicalRect {
1316 start: LogicalPoint::new(mode, inline_start, block_start),
1317 size: LogicalSize::new(mode, inline, block),
1318 debug_writing_mode: DebugWritingMode::new(mode),
1319 }
1320 }
1321
1322 #[inline]
1323 pub fn inline_end(&self) -> T {
1324 self.start.i + self.size.inline
1325 }
1326
1327 #[inline]
1328 pub fn block_end(&self) -> T {
1329 self.start.b + self.size.block
1330 }
1331
1332 #[inline]
1333 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1334 self.debug_writing_mode.check(mode);
1335 let x;
1336 let y;
1337 let width;
1338 let height;
1339 if mode.is_vertical() {
1340 width = self.size.block;
1341 height = self.size.inline;
1342 if mode.is_vertical_lr() {
1343 x = self.start.b;
1344 } else {
1345 x = container_size.width - self.block_end();
1346 }
1347 if mode.is_inline_tb() {
1348 y = self.start.i;
1349 } else {
1350 y = container_size.height - self.inline_end();
1351 }
1352 } else {
1353 width = self.size.inline;
1354 height = self.size.block;
1355 y = self.start.b;
1356 if mode.is_bidi_ltr() {
1357 x = self.start.i;
1358 } else {
1359 x = container_size.width - self.inline_end();
1360 }
1361 }
1362 Rect {
1363 origin: Point2D::new(x, y),
1364 size: Size2D::new(width, height),
1365 }
1366 }
1367
1368 #[inline]
1369 pub fn convert(
1370 &self,
1371 mode_from: WritingMode,
1372 mode_to: WritingMode,
1373 container_size: Size2D<T>,
1374 ) -> LogicalRect<T> {
1375 if mode_from == mode_to {
1376 self.debug_writing_mode.check(mode_from);
1377 *self
1378 } else {
1379 LogicalRect::from_physical(
1380 mode_to,
1381 self.to_physical(mode_from, container_size),
1382 container_size,
1383 )
1384 }
1385 }
1386
1387 pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1388 LogicalRect {
1389 start: self.start + offset,
1390 ..*self
1391 }
1392 }
1393
1394 pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1395 LogicalRect {
1396 start: self.start
1397 + LogicalSize {
1398 inline: offset.i,
1399 block: offset.b,
1400 debug_writing_mode: offset.debug_writing_mode,
1401 },
1402 size: self.size,
1403 debug_writing_mode: self.debug_writing_mode,
1404 }
1405 }
1406}
1407
1408impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1409 #[inline]
1410 pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1411 self.debug_writing_mode
1412 .check_debug(other.debug_writing_mode);
1413
1414 let inline_start = min(self.start.i, other.start.i);
1415 let block_start = min(self.start.b, other.start.b);
1416 LogicalRect {
1417 start: LogicalPoint {
1418 i: inline_start,
1419 b: block_start,
1420 debug_writing_mode: self.debug_writing_mode,
1421 },
1422 size: LogicalSize {
1423 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1424 block: max(self.block_end(), other.block_end()) - block_start,
1425 debug_writing_mode: self.debug_writing_mode,
1426 },
1427 debug_writing_mode: self.debug_writing_mode,
1428 }
1429 }
1430}
1431
1432impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1433 type Output = LogicalRect<T>;
1434
1435 #[inline]
1436 fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1437 self.debug_writing_mode
1438 .check_debug(other.debug_writing_mode);
1439 LogicalRect {
1440 start: LogicalPoint {
1441 i: self.start.i - other.inline_start,
1444 b: self.start.b - other.block_start,
1445 debug_writing_mode: self.debug_writing_mode,
1446 },
1447 size: LogicalSize {
1448 inline: self.size.inline + other.inline_start_end(),
1449 block: self.size.block + other.block_start_end(),
1450 debug_writing_mode: self.debug_writing_mode,
1451 },
1452 debug_writing_mode: self.debug_writing_mode,
1453 }
1454 }
1455}
1456
1457impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1458 type Output = LogicalRect<T>;
1459
1460 #[inline]
1461 fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1462 self.debug_writing_mode
1463 .check_debug(other.debug_writing_mode);
1464 LogicalRect {
1465 start: LogicalPoint {
1466 i: self.start.i + other.inline_start,
1469 b: self.start.b + other.block_start,
1470 debug_writing_mode: self.debug_writing_mode,
1471 },
1472 size: LogicalSize {
1473 inline: self.size.inline - other.inline_start_end(),
1474 block: self.size.block - other.block_start_end(),
1475 debug_writing_mode: self.debug_writing_mode,
1476 },
1477 debug_writing_mode: self.debug_writing_mode,
1478 }
1479 }
1480}
1481
1482#[derive(Clone, Copy, Debug, PartialEq)]
1483#[repr(u8)]
1484pub enum LogicalAxis {
1485 Block = 0,
1486 Inline,
1487}
1488
1489impl LogicalAxis {
1490 #[inline]
1491 pub fn to_physical(self, wm: WritingMode) -> PhysicalAxis {
1492 if wm.is_horizontal() == (self == Self::Inline) {
1493 PhysicalAxis::Horizontal
1494 } else {
1495 PhysicalAxis::Vertical
1496 }
1497 }
1498}
1499
1500#[derive(Clone, Copy, Debug, PartialEq)]
1501#[repr(u8)]
1502pub enum LogicalSide {
1503 BlockStart = 0,
1504 BlockEnd,
1505 InlineStart,
1506 InlineEnd,
1507}
1508
1509impl LogicalSide {
1510 fn is_block(self) -> bool {
1511 matches!(self, Self::BlockStart | Self::BlockEnd)
1512 }
1513
1514 #[inline]
1515 pub fn to_physical(self, wm: WritingMode) -> PhysicalSide {
1516 static BLOCK_MAPPING: [[PhysicalSide; 2]; 4] = [
1518 [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], ];
1523
1524 if self.is_block() {
1525 let vertical = wm.is_vertical();
1526 let lr = wm.is_vertical_lr();
1527 let index = (vertical as usize) | ((lr as usize) << 1);
1528 return BLOCK_MAPPING[index][self as usize];
1529 }
1530
1531 let edge = self as usize - 2;
1533 static INLINE_MAPPING: [[PhysicalSide; 2]; 16] = [
1546 [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], ];
1563
1564 debug_assert!(
1565 WritingMode::VERTICAL.bits() == 0x01
1566 && WritingMode::INLINE_REVERSED.bits() == 0x02
1567 && WritingMode::VERTICAL_LR.bits() == 0x04
1568 && WritingMode::LINE_INVERTED.bits() == 0x08
1569 );
1570 let index = (wm.bits() & 0xF) as usize;
1571 INLINE_MAPPING[index][edge]
1572 }
1573}
1574
1575#[derive(Clone, Copy, Debug, PartialEq)]
1576#[repr(u8)]
1577pub enum LogicalCorner {
1578 StartStart = 0,
1579 StartEnd,
1580 EndStart,
1581 EndEnd,
1582}
1583
1584impl LogicalCorner {
1585 #[inline]
1586 pub fn to_physical(self, wm: WritingMode) -> PhysicalCorner {
1587 static CORNER_TO_SIDES: [[LogicalSide; 2]; 4] = [
1588 [LogicalSide::BlockStart, LogicalSide::InlineStart],
1589 [LogicalSide::BlockStart, LogicalSide::InlineEnd],
1590 [LogicalSide::BlockEnd, LogicalSide::InlineStart],
1591 [LogicalSide::BlockEnd, LogicalSide::InlineEnd],
1592 ];
1593
1594 let [block, inline] = CORNER_TO_SIDES[self as usize];
1595 let block = block.to_physical(wm);
1596 let inline = inline.to_physical(wm);
1597 PhysicalCorner::from_sides(block, inline)
1598 }
1599}
1600
1601#[derive(Clone, Copy, Debug, PartialEq)]
1602#[repr(u8)]
1603pub enum PhysicalAxis {
1604 Vertical = 0,
1605 Horizontal,
1606}
1607
1608#[derive(Clone, Copy, Debug, PartialEq)]
1609#[repr(u8)]
1610pub enum PhysicalSide {
1611 Top = 0,
1612 Right,
1613 Bottom,
1614 Left,
1615}
1616
1617impl PhysicalSide {
1618 pub fn parallel_to(self, other: Self) -> bool {
1620 !self.orthogonal_to(other)
1621 }
1622
1623 pub fn orthogonal_to(self, other: Self) -> bool {
1625 matches!(self, Self::Top | Self::Bottom) != matches!(other, Self::Top | Self::Bottom)
1626 }
1627
1628 pub fn opposite_side(self) -> Self {
1630 match self {
1631 Self::Top => Self::Bottom,
1632 Self::Right => Self::Left,
1633 Self::Bottom => Self::Top,
1634 Self::Left => Self::Right,
1635 }
1636 }
1637}
1638
1639#[derive(Clone, Copy, Debug, PartialEq)]
1640#[repr(u8)]
1641pub enum PhysicalCorner {
1642 TopLeft = 0,
1643 TopRight,
1644 BottomRight,
1645 BottomLeft,
1646}
1647
1648impl PhysicalCorner {
1649 fn from_sides(a: PhysicalSide, b: PhysicalSide) -> Self {
1650 debug_assert!(a.orthogonal_to(b), "Sides should be orthogonal");
1651 const IMPOSSIBLE: PhysicalCorner = PhysicalCorner::TopLeft;
1654 static SIDES_TO_CORNER: [[PhysicalCorner; 4]; 4] = [
1655 [
1656 IMPOSSIBLE,
1657 PhysicalCorner::TopRight,
1658 IMPOSSIBLE,
1659 PhysicalCorner::TopLeft,
1660 ],
1661 [
1662 PhysicalCorner::TopRight,
1663 IMPOSSIBLE,
1664 PhysicalCorner::BottomRight,
1665 IMPOSSIBLE,
1666 ],
1667 [
1668 IMPOSSIBLE,
1669 PhysicalCorner::BottomRight,
1670 IMPOSSIBLE,
1671 PhysicalCorner::BottomLeft,
1672 ],
1673 [
1674 PhysicalCorner::TopLeft,
1675 IMPOSSIBLE,
1676 PhysicalCorner::BottomLeft,
1677 IMPOSSIBLE,
1678 ],
1679 ];
1680 SIDES_TO_CORNER[a as usize][b as usize]
1681 }
1682}