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: inline,
520 block: 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: i,
664 b: 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 {
714 if mode.is_bidi_ltr() {
715 self.i
716 } else {
717 container_size.width - self.i
718 }
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 {
1005 if mode.is_bidi_ltr() {
1006 self.inline_end.clone()
1007 } else {
1008 self.inline_start.clone()
1009 }
1010 }
1011 }
1012
1013 #[inline]
1014 pub fn set_right(&mut self, mode: WritingMode, right: T) {
1015 self.debug_writing_mode.check(mode);
1016 if mode.is_vertical() {
1017 if mode.is_vertical_lr() {
1018 self.block_end = right
1019 } else {
1020 self.block_start = right
1021 }
1022 } else {
1023 if mode.is_bidi_ltr() {
1024 self.inline_end = right
1025 } else {
1026 self.inline_start = right
1027 }
1028 }
1029 }
1030
1031 #[inline]
1032 pub fn bottom(&self, mode: WritingMode) -> T {
1033 self.debug_writing_mode.check(mode);
1034 if mode.is_vertical() {
1035 if mode.is_inline_tb() {
1036 self.inline_end.clone()
1037 } else {
1038 self.inline_start.clone()
1039 }
1040 } else {
1041 self.block_end.clone()
1042 }
1043 }
1044
1045 #[inline]
1046 pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1047 self.debug_writing_mode.check(mode);
1048 if mode.is_vertical() {
1049 if mode.is_inline_tb() {
1050 self.inline_end = bottom
1051 } else {
1052 self.inline_start = bottom
1053 }
1054 } else {
1055 self.block_end = bottom
1056 }
1057 }
1058
1059 #[inline]
1060 pub fn left(&self, mode: WritingMode) -> T {
1061 self.debug_writing_mode.check(mode);
1062 if mode.is_vertical() {
1063 if mode.is_vertical_lr() {
1064 self.block_start.clone()
1065 } else {
1066 self.block_end.clone()
1067 }
1068 } else {
1069 if mode.is_bidi_ltr() {
1070 self.inline_start.clone()
1071 } else {
1072 self.inline_end.clone()
1073 }
1074 }
1075 }
1076
1077 #[inline]
1078 pub fn set_left(&mut self, mode: WritingMode, left: T) {
1079 self.debug_writing_mode.check(mode);
1080 if mode.is_vertical() {
1081 if mode.is_vertical_lr() {
1082 self.block_start = left
1083 } else {
1084 self.block_end = left
1085 }
1086 } else {
1087 if mode.is_bidi_ltr() {
1088 self.inline_start = left
1089 } else {
1090 self.inline_end = left
1091 }
1092 }
1093 }
1094
1095 #[inline]
1096 pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1097 self.debug_writing_mode.check(mode);
1098 let top;
1099 let right;
1100 let bottom;
1101 let left;
1102 if mode.is_vertical() {
1103 if mode.is_vertical_lr() {
1104 left = self.block_start.clone();
1105 right = self.block_end.clone();
1106 } else {
1107 right = self.block_start.clone();
1108 left = self.block_end.clone();
1109 }
1110 if mode.is_inline_tb() {
1111 top = self.inline_start.clone();
1112 bottom = self.inline_end.clone();
1113 } else {
1114 bottom = self.inline_start.clone();
1115 top = self.inline_end.clone();
1116 }
1117 } else {
1118 top = self.block_start.clone();
1119 bottom = self.block_end.clone();
1120 if mode.is_bidi_ltr() {
1121 left = self.inline_start.clone();
1122 right = self.inline_end.clone();
1123 } else {
1124 right = self.inline_start.clone();
1125 left = self.inline_end.clone();
1126 }
1127 }
1128 SideOffsets2D::new(top, right, bottom, left)
1129 }
1130
1131 #[inline]
1132 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1133 if mode_from == mode_to {
1134 self.debug_writing_mode.check(mode_from);
1135 self.clone()
1136 } else {
1137 LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1138 }
1139 }
1140}
1141
1142impl<T: PartialEq + Zero> LogicalMargin<T> {
1143 #[inline]
1144 pub fn is_zero(&self) -> bool {
1145 self.block_start == Zero::zero()
1146 && self.inline_end == Zero::zero()
1147 && self.block_end == Zero::zero()
1148 && self.inline_start == Zero::zero()
1149 }
1150}
1151
1152impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1153 #[inline]
1154 pub fn inline_start_end(&self) -> T {
1155 self.inline_start + self.inline_end
1156 }
1157
1158 #[inline]
1159 pub fn block_start_end(&self) -> T {
1160 self.block_start + self.block_end
1161 }
1162
1163 #[inline]
1164 pub fn start_end(&self, direction: Direction) -> T {
1165 match direction {
1166 Direction::Inline => self.inline_start + self.inline_end,
1167 Direction::Block => self.block_start + self.block_end,
1168 }
1169 }
1170
1171 #[inline]
1172 pub fn top_bottom(&self, mode: WritingMode) -> T {
1173 self.debug_writing_mode.check(mode);
1174 if mode.is_vertical() {
1175 self.inline_start_end()
1176 } else {
1177 self.block_start_end()
1178 }
1179 }
1180
1181 #[inline]
1182 pub fn left_right(&self, mode: WritingMode) -> T {
1183 self.debug_writing_mode.check(mode);
1184 if mode.is_vertical() {
1185 self.block_start_end()
1186 } else {
1187 self.inline_start_end()
1188 }
1189 }
1190}
1191
1192impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1193 type Output = LogicalMargin<T>;
1194
1195 #[inline]
1196 fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1197 self.debug_writing_mode
1198 .check_debug(other.debug_writing_mode);
1199 LogicalMargin {
1200 debug_writing_mode: self.debug_writing_mode,
1201 block_start: self.block_start + other.block_start,
1202 inline_end: self.inline_end + other.inline_end,
1203 block_end: self.block_end + other.block_end,
1204 inline_start: self.inline_start + other.inline_start,
1205 }
1206 }
1207}
1208
1209impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1210 type Output = LogicalMargin<T>;
1211
1212 #[inline]
1213 fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1214 self.debug_writing_mode
1215 .check_debug(other.debug_writing_mode);
1216 LogicalMargin {
1217 debug_writing_mode: self.debug_writing_mode,
1218 block_start: self.block_start - other.block_start,
1219 inline_end: self.inline_end - other.inline_end,
1220 block_end: self.block_end - other.block_end,
1221 inline_start: self.inline_start - other.inline_start,
1222 }
1223 }
1224}
1225
1226#[derive(Clone, Copy, Eq, PartialEq, Serialize)]
1228pub struct LogicalRect<T> {
1229 pub start: LogicalPoint<T>,
1230 pub size: LogicalSize<T>,
1231 debug_writing_mode: DebugWritingMode,
1232}
1233
1234impl<T: Debug> Debug for LogicalRect<T> {
1235 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1236 let writing_mode_string = if cfg!(debug_assertions) {
1237 format!("{:?}, ", self.debug_writing_mode)
1238 } else {
1239 "".to_owned()
1240 };
1241
1242 write!(
1243 formatter,
1244 "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1245 writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1246 )
1247 }
1248}
1249
1250impl<T: Zero> LogicalRect<T> {
1251 #[inline]
1252 pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1253 LogicalRect {
1254 start: LogicalPoint::zero(mode),
1255 size: LogicalSize::zero(mode),
1256 debug_writing_mode: DebugWritingMode::new(mode),
1257 }
1258 }
1259}
1260
1261impl<T: Copy> LogicalRect<T> {
1262 #[inline]
1263 pub fn new(
1264 mode: WritingMode,
1265 inline_start: T,
1266 block_start: T,
1267 inline: T,
1268 block: T,
1269 ) -> LogicalRect<T> {
1270 LogicalRect {
1271 start: LogicalPoint::new(mode, inline_start, block_start),
1272 size: LogicalSize::new(mode, inline, block),
1273 debug_writing_mode: DebugWritingMode::new(mode),
1274 }
1275 }
1276
1277 #[inline]
1278 pub fn from_point_size(
1279 mode: WritingMode,
1280 start: LogicalPoint<T>,
1281 size: LogicalSize<T>,
1282 ) -> LogicalRect<T> {
1283 start.debug_writing_mode.check(mode);
1284 size.debug_writing_mode.check(mode);
1285 LogicalRect {
1286 start: start,
1287 size: size,
1288 debug_writing_mode: DebugWritingMode::new(mode),
1289 }
1290 }
1291}
1292
1293impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1294 #[inline]
1295 pub fn from_physical(
1296 mode: WritingMode,
1297 rect: Rect<T>,
1298 container_size: Size2D<T>,
1299 ) -> LogicalRect<T> {
1300 let inline_start;
1301 let block_start;
1302 let inline;
1303 let block;
1304 if mode.is_vertical() {
1305 inline = rect.size.height;
1306 block = rect.size.width;
1307 if mode.is_vertical_lr() {
1308 block_start = rect.origin.x;
1309 } else {
1310 block_start = container_size.width - (rect.origin.x + rect.size.width);
1311 }
1312 if mode.is_inline_tb() {
1313 inline_start = rect.origin.y;
1314 } else {
1315 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1316 }
1317 } else {
1318 inline = rect.size.width;
1319 block = rect.size.height;
1320 block_start = rect.origin.y;
1321 if mode.is_bidi_ltr() {
1322 inline_start = rect.origin.x;
1323 } else {
1324 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1325 }
1326 }
1327 LogicalRect {
1328 start: LogicalPoint::new(mode, inline_start, block_start),
1329 size: LogicalSize::new(mode, inline, block),
1330 debug_writing_mode: DebugWritingMode::new(mode),
1331 }
1332 }
1333
1334 #[inline]
1335 pub fn inline_end(&self) -> T {
1336 self.start.i + self.size.inline
1337 }
1338
1339 #[inline]
1340 pub fn block_end(&self) -> T {
1341 self.start.b + self.size.block
1342 }
1343
1344 #[inline]
1345 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1346 self.debug_writing_mode.check(mode);
1347 let x;
1348 let y;
1349 let width;
1350 let height;
1351 if mode.is_vertical() {
1352 width = self.size.block;
1353 height = self.size.inline;
1354 if mode.is_vertical_lr() {
1355 x = self.start.b;
1356 } else {
1357 x = container_size.width - self.block_end();
1358 }
1359 if mode.is_inline_tb() {
1360 y = self.start.i;
1361 } else {
1362 y = container_size.height - self.inline_end();
1363 }
1364 } else {
1365 width = self.size.inline;
1366 height = self.size.block;
1367 y = self.start.b;
1368 if mode.is_bidi_ltr() {
1369 x = self.start.i;
1370 } else {
1371 x = container_size.width - self.inline_end();
1372 }
1373 }
1374 Rect {
1375 origin: Point2D::new(x, y),
1376 size: Size2D::new(width, height),
1377 }
1378 }
1379
1380 #[inline]
1381 pub fn convert(
1382 &self,
1383 mode_from: WritingMode,
1384 mode_to: WritingMode,
1385 container_size: Size2D<T>,
1386 ) -> LogicalRect<T> {
1387 if mode_from == mode_to {
1388 self.debug_writing_mode.check(mode_from);
1389 *self
1390 } else {
1391 LogicalRect::from_physical(
1392 mode_to,
1393 self.to_physical(mode_from, container_size),
1394 container_size,
1395 )
1396 }
1397 }
1398
1399 pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1400 LogicalRect {
1401 start: self.start + offset,
1402 ..*self
1403 }
1404 }
1405
1406 pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1407 LogicalRect {
1408 start: self.start
1409 + LogicalSize {
1410 inline: offset.i,
1411 block: offset.b,
1412 debug_writing_mode: offset.debug_writing_mode,
1413 },
1414 size: self.size,
1415 debug_writing_mode: self.debug_writing_mode,
1416 }
1417 }
1418}
1419
1420impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1421 #[inline]
1422 pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1423 self.debug_writing_mode
1424 .check_debug(other.debug_writing_mode);
1425
1426 let inline_start = min(self.start.i, other.start.i);
1427 let block_start = min(self.start.b, other.start.b);
1428 LogicalRect {
1429 start: LogicalPoint {
1430 i: inline_start,
1431 b: block_start,
1432 debug_writing_mode: self.debug_writing_mode,
1433 },
1434 size: LogicalSize {
1435 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1436 block: max(self.block_end(), other.block_end()) - block_start,
1437 debug_writing_mode: self.debug_writing_mode,
1438 },
1439 debug_writing_mode: self.debug_writing_mode,
1440 }
1441 }
1442}
1443
1444impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1445 type Output = LogicalRect<T>;
1446
1447 #[inline]
1448 fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1449 self.debug_writing_mode
1450 .check_debug(other.debug_writing_mode);
1451 LogicalRect {
1452 start: LogicalPoint {
1453 i: self.start.i - other.inline_start,
1456 b: self.start.b - other.block_start,
1457 debug_writing_mode: self.debug_writing_mode,
1458 },
1459 size: LogicalSize {
1460 inline: self.size.inline + other.inline_start_end(),
1461 block: self.size.block + other.block_start_end(),
1462 debug_writing_mode: self.debug_writing_mode,
1463 },
1464 debug_writing_mode: self.debug_writing_mode,
1465 }
1466 }
1467}
1468
1469impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1470 type Output = LogicalRect<T>;
1471
1472 #[inline]
1473 fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1474 self.debug_writing_mode
1475 .check_debug(other.debug_writing_mode);
1476 LogicalRect {
1477 start: LogicalPoint {
1478 i: self.start.i + other.inline_start,
1481 b: self.start.b + other.block_start,
1482 debug_writing_mode: self.debug_writing_mode,
1483 },
1484 size: LogicalSize {
1485 inline: self.size.inline - other.inline_start_end(),
1486 block: self.size.block - other.block_start_end(),
1487 debug_writing_mode: self.debug_writing_mode,
1488 },
1489 debug_writing_mode: self.debug_writing_mode,
1490 }
1491 }
1492}
1493
1494#[derive(Clone, Copy, Debug, PartialEq)]
1495#[repr(u8)]
1496pub enum LogicalAxis {
1497 Block = 0,
1498 Inline,
1499}
1500
1501impl LogicalAxis {
1502 #[inline]
1503 pub fn to_physical(self, wm: WritingMode) -> PhysicalAxis {
1504 if wm.is_horizontal() == (self == Self::Inline) {
1505 PhysicalAxis::Horizontal
1506 } else {
1507 PhysicalAxis::Vertical
1508 }
1509 }
1510}
1511
1512#[derive(Clone, Copy, Debug, PartialEq)]
1513#[repr(u8)]
1514pub enum LogicalSide {
1515 BlockStart = 0,
1516 BlockEnd,
1517 InlineStart,
1518 InlineEnd,
1519}
1520
1521impl LogicalSide {
1522 fn is_block(self) -> bool {
1523 matches!(self, Self::BlockStart | Self::BlockEnd)
1524 }
1525
1526 #[inline]
1527 pub fn to_physical(self, wm: WritingMode) -> PhysicalSide {
1528 static BLOCK_MAPPING: [[PhysicalSide; 2]; 4] = [
1530 [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], ];
1535
1536 if self.is_block() {
1537 let vertical = wm.is_vertical();
1538 let lr = wm.is_vertical_lr();
1539 let index = (vertical as usize) | ((lr as usize) << 1);
1540 return BLOCK_MAPPING[index][self as usize];
1541 }
1542
1543 let edge = self as usize - 2;
1545 static INLINE_MAPPING: [[PhysicalSide; 2]; 16] = [
1558 [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], ];
1575
1576 debug_assert!(
1577 WritingMode::VERTICAL.bits() == 0x01
1578 && WritingMode::INLINE_REVERSED.bits() == 0x02
1579 && WritingMode::VERTICAL_LR.bits() == 0x04
1580 && WritingMode::LINE_INVERTED.bits() == 0x08
1581 );
1582 let index = (wm.bits() & 0xF) as usize;
1583 INLINE_MAPPING[index][edge]
1584 }
1585}
1586
1587#[derive(Clone, Copy, Debug, PartialEq)]
1588#[repr(u8)]
1589pub enum LogicalCorner {
1590 StartStart = 0,
1591 StartEnd,
1592 EndStart,
1593 EndEnd,
1594}
1595
1596impl LogicalCorner {
1597 #[inline]
1598 pub fn to_physical(self, wm: WritingMode) -> PhysicalCorner {
1599 static CORNER_TO_SIDES: [[LogicalSide; 2]; 4] = [
1600 [LogicalSide::BlockStart, LogicalSide::InlineStart],
1601 [LogicalSide::BlockStart, LogicalSide::InlineEnd],
1602 [LogicalSide::BlockEnd, LogicalSide::InlineStart],
1603 [LogicalSide::BlockEnd, LogicalSide::InlineEnd],
1604 ];
1605
1606 let [block, inline] = CORNER_TO_SIDES[self as usize];
1607 let block = block.to_physical(wm);
1608 let inline = inline.to_physical(wm);
1609 PhysicalCorner::from_sides(block, inline)
1610 }
1611}
1612
1613#[derive(Clone, Copy, Debug, PartialEq)]
1614#[repr(u8)]
1615pub enum PhysicalAxis {
1616 Vertical = 0,
1617 Horizontal,
1618}
1619
1620#[derive(Clone, Copy, Debug, PartialEq)]
1621#[repr(u8)]
1622pub enum PhysicalSide {
1623 Top = 0,
1624 Right,
1625 Bottom,
1626 Left,
1627}
1628
1629impl PhysicalSide {
1630 pub fn parallel_to(self, other: Self) -> bool {
1632 !self.orthogonal_to(other)
1633 }
1634
1635 pub fn orthogonal_to(self, other: Self) -> bool {
1637 matches!(self, Self::Top | Self::Bottom) != matches!(other, Self::Top | Self::Bottom)
1638 }
1639
1640 pub fn opposite_side(self) -> Self {
1642 match self {
1643 Self::Top => Self::Bottom,
1644 Self::Right => Self::Left,
1645 Self::Bottom => Self::Top,
1646 Self::Left => Self::Right,
1647 }
1648 }
1649}
1650
1651#[derive(Clone, Copy, Debug, PartialEq)]
1652#[repr(u8)]
1653pub enum PhysicalCorner {
1654 TopLeft = 0,
1655 TopRight,
1656 BottomRight,
1657 BottomLeft,
1658}
1659
1660impl PhysicalCorner {
1661 fn from_sides(a: PhysicalSide, b: PhysicalSide) -> Self {
1662 debug_assert!(a.orthogonal_to(b), "Sides should be orthogonal");
1663 const IMPOSSIBLE: PhysicalCorner = PhysicalCorner::TopLeft;
1666 static SIDES_TO_CORNER: [[PhysicalCorner; 4]; 4] = [
1667 [
1668 IMPOSSIBLE,
1669 PhysicalCorner::TopRight,
1670 IMPOSSIBLE,
1671 PhysicalCorner::TopLeft,
1672 ],
1673 [
1674 PhysicalCorner::TopRight,
1675 IMPOSSIBLE,
1676 PhysicalCorner::BottomRight,
1677 IMPOSSIBLE,
1678 ],
1679 [
1680 IMPOSSIBLE,
1681 PhysicalCorner::BottomRight,
1682 IMPOSSIBLE,
1683 PhysicalCorner::BottomLeft,
1684 ],
1685 [
1686 PhysicalCorner::TopLeft,
1687 IMPOSSIBLE,
1688 PhysicalCorner::BottomLeft,
1689 IMPOSSIBLE,
1690 ],
1691 ];
1692 SIDES_TO_CORNER[a as usize][b as usize]
1693 }
1694}