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