1use crate::{DMat3, DVec2, Mat2, f64::math, swizzles::*};
4use core::fmt;
5use core::iter::{Product, Sum};
6use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
7
8#[cfg(feature = "zerocopy-08")]
9use zerocopy_derive_08::*;
10
11#[inline(always)]
13#[must_use]
14pub const fn dmat2(x_axis: DVec2, y_axis: DVec2) -> DMat2 {
15 DMat2::from_cols(x_axis, y_axis)
16}
17
18#[derive(Clone, Copy)]
20#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
21#[cfg_attr(
22 feature = "zerocopy-08",
23 derive(FromBytes, Immutable, IntoBytes, KnownLayout)
24)]
25#[cfg_attr(feature = "cuda", repr(align(16)))]
26#[repr(C)]
27pub struct DMat2 {
28 pub x_axis: DVec2,
29 pub y_axis: DVec2,
30}
31
32impl DMat2 {
33 pub const ZERO: Self = Self::from_cols(DVec2::ZERO, DVec2::ZERO);
35
36 pub const IDENTITY: Self = Self::from_cols(DVec2::X, DVec2::Y);
38
39 pub const NAN: Self = Self::from_cols(DVec2::NAN, DVec2::NAN);
41
42 #[allow(clippy::too_many_arguments)]
43 #[inline(always)]
44 #[must_use]
45 const fn new(m00: f64, m01: f64, m10: f64, m11: f64) -> Self {
46 Self {
47 x_axis: DVec2::new(m00, m01),
48 y_axis: DVec2::new(m10, m11),
49 }
50 }
51
52 #[inline(always)]
56 #[must_use]
57 pub const fn from_cols(x_axis: DVec2, y_axis: DVec2) -> Self {
58 Self { x_axis, y_axis }
59 }
60
61 #[inline(always)]
67 #[must_use]
68 pub const fn from_rows(row0: DVec2, row1: DVec2) -> Self {
69 let [m00, m01] = row0.to_array();
70 let [m10, m11] = row1.to_array();
71 Self::new(m00, m10, m01, m11)
72 }
73
74 #[inline]
78 #[must_use]
79 pub const fn from_cols_array(m: &[f64; 4]) -> Self {
80 Self::new(m[0], m[1], m[2], m[3])
81 }
82
83 #[inline]
87 #[must_use]
88 pub const fn to_cols_array(&self) -> [f64; 4] {
89 [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
90 }
91
92 #[inline]
96 #[must_use]
97 pub const fn from_cols_array_2d(m: &[[f64; 2]; 2]) -> Self {
98 Self::from_cols(DVec2::from_array(m[0]), DVec2::from_array(m[1]))
99 }
100
101 #[inline]
105 #[must_use]
106 pub const fn to_cols_array_2d(&self) -> [[f64; 2]; 2] {
107 [self.x_axis.to_array(), self.y_axis.to_array()]
108 }
109
110 #[inline]
116 #[must_use]
117 pub const fn from_rows_array(m: &[f64; 4]) -> Self {
118 Self::new(m[0], m[2], m[1], m[3])
119 }
120
121 #[inline]
127 #[must_use]
128 pub const fn to_rows_array(&self) -> [f64; 4] {
129 let m = self.to_cols_array();
130 [m[0], m[2], m[1], m[3]]
131 }
132
133 #[doc(alias = "scale")]
135 #[inline]
136 #[must_use]
137 pub const fn from_diagonal(diagonal: DVec2) -> Self {
138 Self::new(diagonal.x, 0.0, 0.0, diagonal.y)
139 }
140
141 #[inline]
144 #[must_use]
145 pub fn from_scale_angle(scale: DVec2, angle: f64) -> Self {
146 let (sin, cos) = math::sin_cos(angle);
147 Self::new(cos * scale.x, sin * scale.x, -sin * scale.y, cos * scale.y)
148 }
149
150 #[inline]
152 #[must_use]
153 pub fn from_angle(angle: f64) -> Self {
154 let (sin, cos) = math::sin_cos(angle);
155 Self::new(cos, sin, -sin, cos)
156 }
157
158 #[inline]
160 #[must_use]
161 pub fn from_mat3(m: DMat3) -> Self {
162 Self::from_cols(m.x_axis.xy(), m.y_axis.xy())
163 }
164
165 #[inline]
172 #[must_use]
173 #[track_caller]
174 pub fn from_mat3_minor(m: DMat3, i: usize, j: usize) -> Self {
175 match (i, j) {
176 (0, 0) => Self::from_cols(m.y_axis.yz(), m.z_axis.yz()),
177 (0, 1) => Self::from_cols(m.y_axis.xz(), m.z_axis.xz()),
178 (0, 2) => Self::from_cols(m.y_axis.xy(), m.z_axis.xy()),
179 (1, 0) => Self::from_cols(m.x_axis.yz(), m.z_axis.yz()),
180 (1, 1) => Self::from_cols(m.x_axis.xz(), m.z_axis.xz()),
181 (1, 2) => Self::from_cols(m.x_axis.xy(), m.z_axis.xy()),
182 (2, 0) => Self::from_cols(m.x_axis.yz(), m.y_axis.yz()),
183 (2, 1) => Self::from_cols(m.x_axis.xz(), m.y_axis.xz()),
184 (2, 2) => Self::from_cols(m.x_axis.xy(), m.y_axis.xy()),
185 _ => panic!("index out of bounds"),
186 }
187 }
188
189 #[inline]
197 #[must_use]
198 #[track_caller]
199 pub const fn from_cols_slice(slice: &[f64]) -> Self {
200 Self::new(slice[0], slice[1], slice[2], slice[3])
201 }
202
203 #[inline]
209 #[track_caller]
210 pub fn write_cols_to_slice(&self, slice: &mut [f64]) {
211 slice[0] = self.x_axis.x;
212 slice[1] = self.x_axis.y;
213 slice[2] = self.y_axis.x;
214 slice[3] = self.y_axis.y;
215 }
216
217 #[inline]
228 #[must_use]
229 #[track_caller]
230 pub const fn from_rows_slice(slice: &[f64]) -> Self {
231 Self::new(slice[0], slice[2], slice[1], slice[3])
232 }
233
234 #[inline]
240 #[must_use]
241 #[track_caller]
242 pub fn col(&self, index: usize) -> DVec2 {
243 match index {
244 0 => self.x_axis,
245 1 => self.y_axis,
246 _ => panic!("index out of bounds"),
247 }
248 }
249
250 #[inline]
256 #[track_caller]
257 pub fn col_mut(&mut self, index: usize) -> &mut DVec2 {
258 match index {
259 0 => &mut self.x_axis,
260 1 => &mut self.y_axis,
261 _ => panic!("index out of bounds"),
262 }
263 }
264
265 #[inline]
273 #[must_use]
274 #[track_caller]
275 pub fn row(&self, index: usize) -> DVec2 {
276 match index {
277 0 => DVec2::new(self.x_axis.x, self.y_axis.x),
278 1 => DVec2::new(self.x_axis.y, self.y_axis.y),
279 _ => panic!("index out of bounds"),
280 }
281 }
282
283 #[inline]
293 #[track_caller]
294 pub fn set_row(&mut self, index: usize, row: DVec2) {
295 match index {
296 0 => {
297 self.x_axis.x = row.x;
298 self.y_axis.x = row.y;
299 }
300 1 => {
301 self.x_axis.y = row.x;
302 self.y_axis.y = row.y;
303 }
304 _ => panic!("index out of bounds"),
305 }
306 }
307
308 #[inline]
311 #[must_use]
312 pub fn is_finite(&self) -> bool {
313 self.x_axis.is_finite() && self.y_axis.is_finite()
314 }
315
316 #[inline]
318 #[must_use]
319 pub fn is_nan(&self) -> bool {
320 self.x_axis.is_nan() || self.y_axis.is_nan()
321 }
322
323 #[inline]
325 #[must_use]
326 pub fn transpose(&self) -> Self {
327 Self {
328 x_axis: DVec2::new(self.x_axis.x, self.y_axis.x),
329 y_axis: DVec2::new(self.x_axis.y, self.y_axis.y),
330 }
331 }
332
333 #[inline]
335 #[must_use]
336 pub fn diagonal(&self) -> DVec2 {
337 DVec2::new(self.x_axis.x, self.y_axis.y)
338 }
339
340 #[inline]
342 #[must_use]
343 pub fn determinant(&self) -> f64 {
344 self.x_axis.x * self.y_axis.y - self.x_axis.y * self.y_axis.x
345 }
346
347 #[inline(always)]
359 #[must_use]
360 #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
361 fn inverse_checked<const CHECKED: bool>(&self) -> (Self, bool) {
362 let inv_det = 1.0 / self.determinant();
363 let m = Self::new(
364 self.y_axis.y * inv_det,
365 self.x_axis.y * -inv_det,
366 self.y_axis.x * -inv_det,
367 self.x_axis.x * inv_det,
368 );
369 if CHECKED {
370 if !m.is_finite() {
371 return (Self::ZERO, false);
372 }
373 } else {
374 glam_assert!(m.is_finite());
375 }
376 (m, true)
377 }
378
379 #[inline]
391 #[must_use]
392 #[cfg_attr(any(debug_assertions, feature = "glam-assert"), track_caller)]
393 pub fn inverse(&self) -> Self {
394 self.inverse_checked::<false>().0
395 }
396
397 #[inline]
400 #[must_use]
401 pub fn try_inverse(&self) -> Option<Self> {
402 let (m, is_valid) = self.inverse_checked::<true>();
403 if is_valid { Some(m) } else { None }
404 }
405
406 #[inline]
409 #[must_use]
410 pub fn inverse_or_zero(&self) -> Self {
411 self.inverse_checked::<true>().0
412 }
413
414 #[inline]
416 #[must_use]
417 pub fn mul_vec2(&self, rhs: DVec2) -> DVec2 {
418 #[allow(clippy::suspicious_operation_groupings)]
419 DVec2::new(
420 (self.x_axis.x * rhs.x) + (self.y_axis.x * rhs.y),
421 (self.x_axis.y * rhs.x) + (self.y_axis.y * rhs.y),
422 )
423 }
424
425 #[inline]
427 #[must_use]
428 pub fn transpose_mul_vec2(&self, rhs: DVec2) -> DVec2 {
429 DVec2::new(self.x_axis.dot(rhs), self.y_axis.dot(rhs))
430 }
431
432 #[inline]
434 #[must_use]
435 #[deprecated(
436 since = "0.34.2",
437 note = "Use `transpose_mul_vec2` instead, whose word order matches its order of operations and is consistent with `transpose().mul_vec2`."
438 )]
439 pub fn mul_transpose_vec2(&self, rhs: DVec2) -> DVec2 {
440 self.transpose_mul_vec2(rhs)
441 }
442
443 #[inline]
445 #[must_use]
446 pub fn mul_mat2(&self, rhs: &Self) -> Self {
447 self.mul(rhs)
448 }
449
450 #[inline]
452 #[must_use]
453 pub fn add_mat2(&self, rhs: &Self) -> Self {
454 self.add(rhs)
455 }
456
457 #[inline]
459 #[must_use]
460 pub fn sub_mat2(&self, rhs: &Self) -> Self {
461 self.sub(rhs)
462 }
463
464 #[inline]
466 #[must_use]
467 pub fn mul_scalar(&self, rhs: f64) -> Self {
468 Self::from_cols(self.x_axis.mul(rhs), self.y_axis.mul(rhs))
469 }
470
471 #[inline]
475 #[must_use]
476 pub fn mul_diagonal_scale(&self, scale: DVec2) -> Self {
477 Self::from_cols(self.x_axis * scale.x, self.y_axis * scale.y)
478 }
479
480 #[inline]
482 #[must_use]
483 pub fn div_scalar(&self, rhs: f64) -> Self {
484 let rhs = DVec2::splat(rhs);
485 Self::from_cols(self.x_axis.div(rhs), self.y_axis.div(rhs))
486 }
487
488 #[inline]
490 #[must_use]
491 pub fn recip(&self) -> Self {
492 Self::from_cols(self.x_axis.recip(), self.y_axis.recip())
493 }
494
495 #[inline]
505 #[must_use]
506 pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: f64) -> bool {
507 self.x_axis.abs_diff_eq(rhs.x_axis, max_abs_diff)
508 && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
509 }
510
511 #[inline]
513 #[must_use]
514 pub fn abs(&self) -> Self {
515 Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
516 }
517
518 #[cfg(feature = "f64")]
519 #[inline]
520 #[must_use]
521 pub fn as_mat2(&self) -> Mat2 {
522 Mat2::from_cols(self.x_axis.as_vec2(), self.y_axis.as_vec2())
523 }
524}
525
526impl Default for DMat2 {
527 #[inline]
528 fn default() -> Self {
529 Self::IDENTITY
530 }
531}
532
533impl Add for DMat2 {
534 type Output = Self;
535 #[inline]
536 fn add(self, rhs: Self) -> Self {
537 Self::from_cols(self.x_axis.add(rhs.x_axis), self.y_axis.add(rhs.y_axis))
538 }
539}
540
541impl Add<&Self> for DMat2 {
542 type Output = Self;
543 #[inline]
544 fn add(self, rhs: &Self) -> Self {
545 self.add(*rhs)
546 }
547}
548
549impl Add<&DMat2> for &DMat2 {
550 type Output = DMat2;
551 #[inline]
552 fn add(self, rhs: &DMat2) -> DMat2 {
553 (*self).add(*rhs)
554 }
555}
556
557impl Add<DMat2> for &DMat2 {
558 type Output = DMat2;
559 #[inline]
560 fn add(self, rhs: DMat2) -> DMat2 {
561 (*self).add(rhs)
562 }
563}
564
565impl AddAssign for DMat2 {
566 #[inline]
567 fn add_assign(&mut self, rhs: Self) {
568 *self = self.add(rhs);
569 }
570}
571
572impl AddAssign<&Self> for DMat2 {
573 #[inline]
574 fn add_assign(&mut self, rhs: &Self) {
575 self.add_assign(*rhs);
576 }
577}
578
579impl Sub for DMat2 {
580 type Output = Self;
581 #[inline]
582 fn sub(self, rhs: Self) -> Self {
583 Self::from_cols(self.x_axis.sub(rhs.x_axis), self.y_axis.sub(rhs.y_axis))
584 }
585}
586
587impl Sub<&Self> for DMat2 {
588 type Output = Self;
589 #[inline]
590 fn sub(self, rhs: &Self) -> Self {
591 self.sub(*rhs)
592 }
593}
594
595impl Sub<&DMat2> for &DMat2 {
596 type Output = DMat2;
597 #[inline]
598 fn sub(self, rhs: &DMat2) -> DMat2 {
599 (*self).sub(*rhs)
600 }
601}
602
603impl Sub<DMat2> for &DMat2 {
604 type Output = DMat2;
605 #[inline]
606 fn sub(self, rhs: DMat2) -> DMat2 {
607 (*self).sub(rhs)
608 }
609}
610
611impl SubAssign for DMat2 {
612 #[inline]
613 fn sub_assign(&mut self, rhs: Self) {
614 *self = self.sub(rhs);
615 }
616}
617
618impl SubAssign<&Self> for DMat2 {
619 #[inline]
620 fn sub_assign(&mut self, rhs: &Self) {
621 self.sub_assign(*rhs);
622 }
623}
624
625impl Neg for DMat2 {
626 type Output = Self;
627 #[inline]
628 fn neg(self) -> Self::Output {
629 Self::from_cols(self.x_axis.neg(), self.y_axis.neg())
630 }
631}
632
633impl Neg for &DMat2 {
634 type Output = DMat2;
635 #[inline]
636 fn neg(self) -> DMat2 {
637 (*self).neg()
638 }
639}
640
641impl Mul for DMat2 {
642 type Output = Self;
643 #[inline]
644 fn mul(self, rhs: Self) -> Self {
645 Self::from_cols(self.mul(rhs.x_axis), self.mul(rhs.y_axis))
646 }
647}
648
649impl Mul<&Self> for DMat2 {
650 type Output = Self;
651 #[inline]
652 fn mul(self, rhs: &Self) -> Self {
653 self.mul(*rhs)
654 }
655}
656
657impl Mul<&DMat2> for &DMat2 {
658 type Output = DMat2;
659 #[inline]
660 fn mul(self, rhs: &DMat2) -> DMat2 {
661 (*self).mul(*rhs)
662 }
663}
664
665impl Mul<DMat2> for &DMat2 {
666 type Output = DMat2;
667 #[inline]
668 fn mul(self, rhs: DMat2) -> DMat2 {
669 (*self).mul(rhs)
670 }
671}
672
673impl MulAssign for DMat2 {
674 #[inline]
675 fn mul_assign(&mut self, rhs: Self) {
676 *self = self.mul(rhs);
677 }
678}
679
680impl MulAssign<&Self> for DMat2 {
681 #[inline]
682 fn mul_assign(&mut self, rhs: &Self) {
683 self.mul_assign(*rhs);
684 }
685}
686
687impl Mul<DVec2> for DMat2 {
688 type Output = DVec2;
689 #[inline]
690 fn mul(self, rhs: DVec2) -> Self::Output {
691 self.mul_vec2(rhs)
692 }
693}
694
695impl Mul<&DVec2> for DMat2 {
696 type Output = DVec2;
697 #[inline]
698 fn mul(self, rhs: &DVec2) -> DVec2 {
699 self.mul(*rhs)
700 }
701}
702
703impl Mul<&DVec2> for &DMat2 {
704 type Output = DVec2;
705 #[inline]
706 fn mul(self, rhs: &DVec2) -> DVec2 {
707 (*self).mul(*rhs)
708 }
709}
710
711impl Mul<DVec2> for &DMat2 {
712 type Output = DVec2;
713 #[inline]
714 fn mul(self, rhs: DVec2) -> DVec2 {
715 (*self).mul(rhs)
716 }
717}
718
719impl Mul<DMat2> for f64 {
720 type Output = DMat2;
721 #[inline]
722 fn mul(self, rhs: DMat2) -> Self::Output {
723 rhs.mul_scalar(self)
724 }
725}
726
727impl Mul<&DMat2> for f64 {
728 type Output = DMat2;
729 #[inline]
730 fn mul(self, rhs: &DMat2) -> DMat2 {
731 self.mul(*rhs)
732 }
733}
734
735impl Mul<&DMat2> for &f64 {
736 type Output = DMat2;
737 #[inline]
738 fn mul(self, rhs: &DMat2) -> DMat2 {
739 (*self).mul(*rhs)
740 }
741}
742
743impl Mul<DMat2> for &f64 {
744 type Output = DMat2;
745 #[inline]
746 fn mul(self, rhs: DMat2) -> DMat2 {
747 (*self).mul(rhs)
748 }
749}
750
751impl Mul<f64> for DMat2 {
752 type Output = Self;
753 #[inline]
754 fn mul(self, rhs: f64) -> Self {
755 self.mul_scalar(rhs)
756 }
757}
758
759impl Mul<&f64> for DMat2 {
760 type Output = Self;
761 #[inline]
762 fn mul(self, rhs: &f64) -> Self {
763 self.mul(*rhs)
764 }
765}
766
767impl Mul<&f64> for &DMat2 {
768 type Output = DMat2;
769 #[inline]
770 fn mul(self, rhs: &f64) -> DMat2 {
771 (*self).mul(*rhs)
772 }
773}
774
775impl Mul<f64> for &DMat2 {
776 type Output = DMat2;
777 #[inline]
778 fn mul(self, rhs: f64) -> DMat2 {
779 (*self).mul(rhs)
780 }
781}
782
783impl MulAssign<f64> for DMat2 {
784 #[inline]
785 fn mul_assign(&mut self, rhs: f64) {
786 *self = self.mul(rhs);
787 }
788}
789
790impl MulAssign<&f64> for DMat2 {
791 #[inline]
792 fn mul_assign(&mut self, rhs: &f64) {
793 self.mul_assign(*rhs);
794 }
795}
796
797impl Div<DMat2> for f64 {
798 type Output = DMat2;
799 #[inline]
800 fn div(self, rhs: DMat2) -> Self::Output {
801 DMat2::from_cols(self.div(rhs.x_axis), self.div(rhs.y_axis))
802 }
803}
804
805impl Div<&DMat2> for f64 {
806 type Output = DMat2;
807 #[inline]
808 fn div(self, rhs: &DMat2) -> DMat2 {
809 self.div(*rhs)
810 }
811}
812
813impl Div<&DMat2> for &f64 {
814 type Output = DMat2;
815 #[inline]
816 fn div(self, rhs: &DMat2) -> DMat2 {
817 (*self).div(*rhs)
818 }
819}
820
821impl Div<DMat2> for &f64 {
822 type Output = DMat2;
823 #[inline]
824 fn div(self, rhs: DMat2) -> DMat2 {
825 (*self).div(rhs)
826 }
827}
828
829impl Div<f64> for DMat2 {
830 type Output = Self;
831 #[inline]
832 fn div(self, rhs: f64) -> Self {
833 self.div_scalar(rhs)
834 }
835}
836
837impl Div<&f64> for DMat2 {
838 type Output = Self;
839 #[inline]
840 fn div(self, rhs: &f64) -> Self {
841 self.div(*rhs)
842 }
843}
844
845impl Div<&f64> for &DMat2 {
846 type Output = DMat2;
847 #[inline]
848 fn div(self, rhs: &f64) -> DMat2 {
849 (*self).div(*rhs)
850 }
851}
852
853impl Div<f64> for &DMat2 {
854 type Output = DMat2;
855 #[inline]
856 fn div(self, rhs: f64) -> DMat2 {
857 (*self).div(rhs)
858 }
859}
860
861impl DivAssign<f64> for DMat2 {
862 #[inline]
863 fn div_assign(&mut self, rhs: f64) {
864 *self = self.div(rhs);
865 }
866}
867
868impl DivAssign<&f64> for DMat2 {
869 #[inline]
870 fn div_assign(&mut self, rhs: &f64) {
871 self.div_assign(*rhs);
872 }
873}
874
875impl Sum<Self> for DMat2 {
876 fn sum<I>(iter: I) -> Self
877 where
878 I: Iterator<Item = Self>,
879 {
880 iter.fold(Self::ZERO, Self::add)
881 }
882}
883
884impl<'a> Sum<&'a Self> for DMat2 {
885 fn sum<I>(iter: I) -> Self
886 where
887 I: Iterator<Item = &'a Self>,
888 {
889 iter.fold(Self::ZERO, |a, &b| Self::add(a, b))
890 }
891}
892
893impl Product for DMat2 {
894 fn product<I>(iter: I) -> Self
895 where
896 I: Iterator<Item = Self>,
897 {
898 iter.fold(Self::IDENTITY, Self::mul)
899 }
900}
901
902impl<'a> Product<&'a Self> for DMat2 {
903 fn product<I>(iter: I) -> Self
904 where
905 I: Iterator<Item = &'a Self>,
906 {
907 iter.fold(Self::IDENTITY, |a, &b| Self::mul(a, b))
908 }
909}
910
911impl PartialEq for DMat2 {
912 #[inline]
913 fn eq(&self, rhs: &Self) -> bool {
914 self.x_axis.eq(&rhs.x_axis) && self.y_axis.eq(&rhs.y_axis)
915 }
916}
917
918impl AsRef<[f64; 4]> for DMat2 {
919 #[inline]
920 fn as_ref(&self) -> &[f64; 4] {
921 unsafe { &*(self as *const Self as *const [f64; 4]) }
922 }
923}
924
925impl AsMut<[f64; 4]> for DMat2 {
926 #[inline]
927 fn as_mut(&mut self) -> &mut [f64; 4] {
928 unsafe { &mut *(self as *mut Self as *mut [f64; 4]) }
929 }
930}
931
932impl fmt::Debug for DMat2 {
933 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
934 fmt.debug_struct(stringify!(DMat2))
935 .field("x_axis", &self.x_axis)
936 .field("y_axis", &self.y_axis)
937 .finish()
938 }
939}
940
941impl fmt::Display for DMat2 {
942 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943 if let Some(p) = f.precision() {
944 write!(f, "[{:.*}, {:.*}]", p, self.x_axis, p, self.y_axis)
945 } else {
946 write!(f, "[{}, {}]", self.x_axis, self.y_axis)
947 }
948 }
949}