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