1use std::collections::HashMap;
10use std::fmt;
11
12use crate::component::ComponentId;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
16pub struct Point {
17 pub x: f32,
18 pub y: f32,
19}
20
21impl Point {
22 pub fn new(x: f32, y: f32) -> Self {
23 Self { x, y }
24 }
25
26 pub fn zero() -> Self {
27 Self::new(0.0, 0.0)
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq)]
33pub struct Size {
34 pub width: f32,
35 pub height: f32,
36}
37
38impl Size {
39 pub fn new(width: f32, height: f32) -> Self {
40 Self { width, height }
41 }
42
43 pub fn zero() -> Self {
44 Self::new(0.0, 0.0)
45 }
46
47 pub fn area(&self) -> f32 {
48 self.width * self.height
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq)]
54pub struct Rect {
55 pub origin: Point,
56 pub size: Size,
57}
58
59impl Rect {
60 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
61 Self {
62 origin: Point::new(x, y),
63 size: Size::new(width, height),
64 }
65 }
66
67 pub fn zero() -> Self {
68 Self {
69 origin: Point::zero(),
70 size: Size::zero(),
71 }
72 }
73
74 pub fn x(&self) -> f32 {
75 self.origin.x
76 }
77
78 pub fn y(&self) -> f32 {
79 self.origin.y
80 }
81
82 pub fn width(&self) -> f32 {
83 self.size.width
84 }
85
86 pub fn height(&self) -> f32 {
87 self.size.height
88 }
89
90 pub fn max_x(&self) -> f32 {
91 self.origin.x + self.size.width
92 }
93
94 pub fn max_y(&self) -> f32 {
95 self.origin.y + self.size.height
96 }
97
98 pub fn contains_point(&self, point: Point) -> bool {
99 point.x >= self.x()
100 && point.x <= self.max_x()
101 && point.y >= self.y()
102 && point.y <= self.max_y()
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum FlexDirection {
109 #[default]
110 Row,
111 Column,
112 RowReverse,
113 ColumnReverse,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
118pub enum FlexWrap {
119 #[default]
120 NoWrap,
121 Wrap,
122 WrapReverse,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum JustifyContent {
128 #[default]
129 FlexStart,
130 FlexEnd,
131 Center,
132 SpaceBetween,
133 SpaceAround,
134 SpaceEvenly,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
139pub enum AlignItems {
140 FlexStart,
141 FlexEnd,
142 Center,
143 #[default]
144 Stretch,
145 Baseline,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
150pub enum AlignContent {
151 FlexStart,
152 FlexEnd,
153 Center,
154 SpaceBetween,
155 SpaceAround,
156 #[default]
157 Stretch,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Default)]
162pub enum Dimension {
163 #[default]
164 Auto,
165 Points(f32),
166 Percent(f32),
167}
168
169impl Dimension {
170 pub fn resolve(&self, container_size: f32) -> f32 {
172 match self {
173 Dimension::Auto => 0.0, Dimension::Points(points) => *points,
175 Dimension::Percent(percent) => container_size * percent / 100.0,
176 }
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq)]
182pub struct EdgeValues {
183 pub top: f32,
184 pub right: f32,
185 pub bottom: f32,
186 pub left: f32,
187}
188
189impl EdgeValues {
190 pub fn new(top: f32, right: f32, bottom: f32, left: f32) -> Self {
191 Self {
192 top,
193 right,
194 bottom,
195 left,
196 }
197 }
198
199 pub fn uniform(value: f32) -> Self {
200 Self::new(value, value, value, value)
201 }
202
203 pub fn horizontal_vertical(horizontal: f32, vertical: f32) -> Self {
204 Self::new(vertical, horizontal, vertical, horizontal)
205 }
206
207 pub fn zero() -> Self {
208 Self::uniform(0.0)
209 }
210
211 pub fn horizontal(&self) -> f32 {
212 self.left + self.right
213 }
214
215 pub fn vertical(&self) -> f32 {
216 self.top + self.bottom
217 }
218}
219
220impl Default for EdgeValues {
221 fn default() -> Self {
222 Self::zero()
223 }
224}
225
226#[derive(Debug, Clone, PartialEq)]
228pub struct LayoutStyle {
229 pub position_type: PositionType,
231 pub top: Dimension,
232 pub right: Dimension,
233 pub bottom: Dimension,
234 pub left: Dimension,
235
236 pub width: Dimension,
238 pub height: Dimension,
239 pub min_width: Dimension,
240 pub min_height: Dimension,
241 pub max_width: Dimension,
242 pub max_height: Dimension,
243
244 pub flex_direction: FlexDirection,
246 pub flex_wrap: FlexWrap,
247 pub justify_content: JustifyContent,
248 pub align_items: AlignItems,
249 pub align_content: AlignContent,
250
251 pub flex_grow: f32,
253 pub flex_shrink: f32,
254 pub flex_basis: Dimension,
255 pub align_self: Option<AlignItems>,
256
257 pub margin: EdgeValues,
259 pub padding: EdgeValues,
260 pub border: EdgeValues, pub gap: Gap,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
265pub enum PositionType {
266 #[default]
267 Relative,
268 Absolute,
269}
270
271impl Default for LayoutStyle {
272 fn default() -> Self {
273 Self {
274 position_type: PositionType::default(),
275 top: Dimension::default(),
276 right: Dimension::default(),
277 bottom: Dimension::default(),
278 left: Dimension::default(),
279 width: Dimension::default(),
280 height: Dimension::default(),
281 min_width: Dimension::default(),
282 min_height: Dimension::default(),
283 max_width: Dimension::default(),
284 max_height: Dimension::default(),
285 flex_direction: FlexDirection::default(),
286 flex_wrap: FlexWrap::default(),
287 justify_content: JustifyContent::default(),
288 align_items: AlignItems::default(),
289 align_content: AlignContent::default(),
290 flex_grow: 0.0,
291 flex_shrink: 1.0,
292 flex_basis: Dimension::Auto,
293 align_self: None,
294 margin: EdgeValues::default(),
295 padding: EdgeValues::default(),
296 border: EdgeValues::default(),
297 gap: Gap::default(),
298 }
299 }
300}
301
302#[derive(Debug, Clone, PartialEq)]
304pub struct LayoutResult {
305 pub rect: Rect,
307 pub content_rect: Rect,
309 pub is_dirty: bool,
311}
312
313impl Default for LayoutResult {
314 fn default() -> Self {
315 Self {
316 rect: Rect::zero(),
317 content_rect: Rect::zero(),
318 is_dirty: true,
319 }
320 }
321}
322
323#[derive(Debug)]
325pub struct LayoutNode {
326 pub id: ComponentId,
328 pub style: LayoutStyle,
330 pub layout: LayoutResult,
332 pub children: Vec<LayoutNode>,
334 pub parent_id: Option<ComponentId>,
336}
337
338impl LayoutNode {
339 pub fn new(id: ComponentId, style: LayoutStyle) -> Self {
341 Self {
342 id,
343 style,
344 layout: LayoutResult::default(),
345 children: Vec::new(),
346 parent_id: None,
347 }
348 }
349
350 pub fn add_child(&mut self, mut child: LayoutNode) {
352 child.parent_id = Some(self.id);
353 self.children.push(child);
354 self.mark_dirty();
355 }
356
357 pub fn remove_child(&mut self, child_id: ComponentId) -> Option<LayoutNode> {
359 if let Some(index) = self.children.iter().position(|child| child.id == child_id) {
360 let mut child = self.children.remove(index);
361 child.parent_id = None;
362 self.mark_dirty();
363 Some(child)
364 } else {
365 None
366 }
367 }
368
369 pub fn mark_dirty(&mut self) {
371 self.layout.is_dirty = true;
372 }
374
375 pub fn is_dirty(&self) -> bool {
377 self.layout.is_dirty || self.children.iter().any(|child| child.is_dirty())
378 }
379
380 pub fn main_axis_size(&self) -> f32 {
382 match self.style.flex_direction {
383 FlexDirection::Row | FlexDirection::RowReverse => self.layout.rect.width(),
384 FlexDirection::Column | FlexDirection::ColumnReverse => self.layout.rect.height(),
385 }
386 }
387
388 pub fn cross_axis_size(&self) -> f32 {
390 match self.style.flex_direction {
391 FlexDirection::Row | FlexDirection::RowReverse => self.layout.rect.height(),
392 FlexDirection::Column | FlexDirection::ColumnReverse => self.layout.rect.width(),
393 }
394 }
395}
396
397#[derive(Debug)]
399pub struct LayoutEngine {
400 layout_cache: HashMap<ComponentId, LayoutResult>,
402 pub stats: LayoutStats,
404}
405
406#[derive(Debug, Default, Clone)]
408pub struct LayoutStats {
409 pub layout_calculations: u64,
411 pub layout_time_us: u64,
413 pub cache_hits: u64,
415 pub cache_misses: u64,
417 pub node_count: u32,
419}
420
421impl LayoutEngine {
422 pub fn new() -> Self {
424 Self {
425 layout_cache: HashMap::new(),
426 stats: LayoutStats::default(),
427 }
428 }
429
430 pub fn calculate_layout(
432 &mut self,
433 root: &mut LayoutNode,
434 container_size: Size,
435 ) -> Result<(), LayoutError> {
436 let start_time = std::time::Instant::now();
437
438 self.prepare_layout(root);
440
441 self.layout_node(root, container_size)?;
443
444 let elapsed = start_time.elapsed();
446 self.stats.layout_calculations += 1;
447 self.stats.layout_time_us += elapsed.as_micros() as u64;
448
449 Ok(())
450 }
451
452 fn prepare_layout(&mut self, node: &mut LayoutNode) {
454 if node.layout.is_dirty {
455 self.layout_cache.remove(&node.id);
457 }
458
459 self.stats.node_count = self.count_nodes(node);
461
462 for child in &mut node.children {
464 self.prepare_layout(child);
465 }
466 }
467
468 #[allow(clippy::only_used_in_recursion)]
470 fn count_nodes(&self, node: &LayoutNode) -> u32 {
471 1 + node
472 .children
473 .iter()
474 .map(|child| self.count_nodes(child))
475 .sum::<u32>()
476 }
477
478 fn layout_node(
480 &mut self,
481 node: &mut LayoutNode,
482 container_size: Size,
483 ) -> Result<(), LayoutError> {
484 if !node.layout.is_dirty {
486 if let Some(cached_layout) = self.layout_cache.get(&node.id) {
487 node.layout = cached_layout.clone();
488 self.stats.cache_hits += 1;
489 return Ok(());
490 }
491 }
492
493 self.stats.cache_misses += 1;
494
495 self.calculate_node_size(node, container_size)?;
497 self.calculate_node_position(node)?;
498
499 if !node.children.is_empty() {
501 self.layout_flex_children(node)?;
502 }
503
504 node.layout.is_dirty = false;
506 self.layout_cache.insert(node.id, node.layout.clone());
507
508 Ok(())
509 }
510
511 fn calculate_node_size(
513 &self,
514 node: &mut LayoutNode,
515 container_size: Size,
516 ) -> Result<(), LayoutError> {
517 let style = &node.style;
518
519 let width = match style.width {
521 Dimension::Auto => {
522 container_size.width - style.margin.horizontal() - style.padding.horizontal()
525 }
526 _ => style.width.resolve(container_size.width),
527 };
528
529 let height = match style.height {
531 Dimension::Auto => {
532 0.0
535 }
536 _ => style.height.resolve(container_size.height),
537 };
538
539 let final_width = {
541 let mut w = width;
542 if !matches!(style.min_width, Dimension::Auto) {
543 w = w.max(style.min_width.resolve(container_size.width));
544 }
545 if !matches!(style.max_width, Dimension::Auto) {
546 w = w.min(style.max_width.resolve(container_size.width));
547 }
548 w
549 };
550
551 let final_height = {
552 let mut h = height;
553 if !matches!(style.min_height, Dimension::Auto) {
554 h = h.max(style.min_height.resolve(container_size.height));
555 }
556 if !matches!(style.max_height, Dimension::Auto) {
557 h = h.min(style.max_height.resolve(container_size.height));
558 }
559 h
560 };
561
562 node.layout.rect.size = Size::new(final_width, final_height);
564
565 let content_x = node.layout.rect.x() + style.padding.left + style.border.left;
567 let content_y = node.layout.rect.y() + style.padding.top + style.border.top;
568 let content_width = final_width - style.padding.horizontal() - style.border.horizontal();
569 let content_height = final_height - style.padding.vertical() - style.border.vertical();
570
571 node.layout.content_rect = Rect::new(content_x, content_y, content_width, content_height);
572
573 Ok(())
574 }
575
576 fn calculate_node_position(&self, node: &mut LayoutNode) -> Result<(), LayoutError> {
578 let style = &node.style;
579
580 match style.position_type {
581 PositionType::Relative => {
582 }
585 PositionType::Absolute => {
586 let x = style.left.resolve(0.0); let y = style.top.resolve(0.0);
589 node.layout.rect.origin = Point::new(x, y);
590 }
591 }
592
593 Ok(())
594 }
595 fn layout_flex_children(&mut self, parent: &mut LayoutNode) -> Result<(), LayoutError> {
597 if parent.children.is_empty() {
598 return Ok(());
599 }
600
601 let parent_content_size = parent.layout.content_rect.size;
602 let parent_style = &parent.style;
603
604 let (absolute_children, relative_children): (Vec<_>, Vec<_>) = (0..parent.children.len())
606 .partition(|&i| parent.children[i].style.position_type == PositionType::Absolute);
607
608 if !relative_children.is_empty() {
610 if parent_style.flex_wrap == FlexWrap::Wrap
612 || parent_style.flex_wrap == FlexWrap::WrapReverse
613 {
614 self.layout_flex_multiline(
615 &mut parent.children,
616 &relative_children,
617 parent_content_size,
618 parent_style,
619 )?;
620 } else {
621 self.layout_flex_line(
622 &mut parent.children,
623 &relative_children,
624 parent_content_size,
625 parent_style,
626 )?;
627 }
628 }
629
630 for &child_index in &absolute_children {
632 let child = &mut parent.children[child_index];
633 self.layout_node(child, parent_content_size)?;
634 }
635
636 Ok(())
637 }
638 fn layout_flex_line(
640 &mut self,
641 children: &mut [LayoutNode],
642 child_indices: &[usize],
643 container_size: Size,
644 parent_style: &LayoutStyle,
645 ) -> Result<(), LayoutError> {
646 let flex_direction = parent_style.flex_direction;
647 let is_row = matches!(
648 flex_direction,
649 FlexDirection::Row | FlexDirection::RowReverse
650 );
651 let is_reverse = matches!(
652 flex_direction,
653 FlexDirection::RowReverse | FlexDirection::ColumnReverse
654 );
655
656 let main_axis_size = if is_row {
657 container_size.width
658 } else {
659 container_size.height
660 };
661 let cross_axis_size = if is_row {
662 container_size.height
663 } else {
664 container_size.width
665 };
666
667 let mut item_data = Vec::new();
669 let mut total_basis_size = 0.0;
670 let mut total_flex_grow = 0.0;
671 let mut total_flex_shrink = 0.0;
672
673 for &child_index in child_indices {
674 let child = &children[child_index];
675
676 let basis_size = self.calculate_flex_basis_size(child, container_size, is_row)?;
678 let main_size = self.resolve_main_size(child, basis_size, is_row);
679 let cross_size = self.resolve_cross_size(child, cross_axis_size, is_row);
680
681 total_basis_size += main_size;
682 total_flex_grow += child.style.flex_grow;
683 total_flex_shrink += child.style.flex_shrink;
684
685 item_data.push(FlexItemData {
686 index: child_index,
687 basis_size,
688 main_size,
689 cross_size,
690 flex_grow: child.style.flex_grow,
691 flex_shrink: child.style.flex_shrink,
692 });
693 }
694
695 let gap_size = if is_row {
697 parent_style.gap.column
698 } else {
699 parent_style.gap.row
700 };
701 let total_gaps = gap_size * (child_indices.len() as f32 - 1.0).max(0.0);
702 let available_space = main_axis_size - total_basis_size - total_gaps;
703
704 self.distribute_flex_space(
706 &mut item_data,
707 available_space,
708 total_flex_grow,
709 total_flex_shrink,
710 )?;
711
712 let positions = self.calculate_justify_content_positions(
714 &item_data,
715 main_axis_size,
716 gap_size,
717 parent_style.justify_content,
718 is_reverse,
719 );
720
721 for (i, &child_index) in child_indices.iter().enumerate() {
723 let child = &mut children[child_index];
724 let item = &item_data[i];
725 let main_pos = positions[i];
726
727 let cross_pos = self.calculate_cross_axis_position(
729 child,
730 item.cross_size,
731 cross_axis_size,
732 parent_style.align_items,
733 );
734
735 if is_row {
737 child.layout.rect = Rect::new(main_pos, cross_pos, item.main_size, item.cross_size);
738 } else {
739 child.layout.rect = Rect::new(cross_pos, main_pos, item.cross_size, item.main_size);
740 }
741
742 self.layout_node(child, child.layout.rect.size)?;
744 }
745
746 Ok(())
747 }
748 pub fn clear_cache(&mut self) {
750 self.layout_cache.clear();
751 }
752
753 pub fn get_stats(&self) -> &LayoutStats {
755 &self.stats
756 }
757
758 pub fn reset_stats(&mut self) {
760 self.stats = LayoutStats::default();
761 }
762
763 fn calculate_flex_basis_size(
767 &self,
768 child: &LayoutNode,
769 container_size: Size,
770 is_row: bool,
771 ) -> Result<f32, LayoutError> {
772 let basis_size = match child.style.flex_basis {
773 Dimension::Auto => {
774 if is_row {
776 child.style.width.resolve(container_size.width)
777 } else {
778 child.style.height.resolve(container_size.height)
779 }
780 }
781 _ => {
782 let main_axis_size = if is_row {
783 container_size.width
784 } else {
785 container_size.height
786 };
787 child.style.flex_basis.resolve(main_axis_size)
788 }
789 };
790 Ok(basis_size)
791 }
792
793 fn resolve_main_size(&self, child: &LayoutNode, basis_size: f32, is_row: bool) -> f32 {
795 let explicit_size = if is_row {
796 match child.style.width {
797 Dimension::Auto => basis_size,
798 _ => child.style.width.resolve(0.0), }
800 } else {
801 match child.style.height {
802 Dimension::Auto => basis_size,
803 _ => child.style.height.resolve(0.0),
804 }
805 };
806 explicit_size.max(basis_size)
807 }
808
809 fn resolve_cross_size(&self, child: &LayoutNode, cross_axis_size: f32, is_row: bool) -> f32 {
811 if is_row {
812 match child.style.height {
813 Dimension::Auto => cross_axis_size, _ => child.style.height.resolve(cross_axis_size),
815 }
816 } else {
817 match child.style.width {
818 Dimension::Auto => cross_axis_size,
819 _ => child.style.width.resolve(cross_axis_size),
820 }
821 }
822 }
823
824 fn distribute_flex_space(
826 &self,
827 items: &mut [FlexItemData],
828 available_space: f32,
829 total_flex_grow: f32,
830 total_flex_shrink: f32,
831 ) -> Result<(), LayoutError> {
832 if available_space > 0.0 && total_flex_grow > 0.0 {
833 for item in items.iter_mut() {
835 if item.flex_grow > 0.0 {
836 let growth = available_space * (item.flex_grow / total_flex_grow);
837 item.main_size += growth;
838 }
839 }
840 } else if available_space < 0.0 && total_flex_shrink > 0.0 {
841 for item in items.iter_mut() {
843 if item.flex_shrink > 0.0 {
844 let shrinkage = available_space * (item.flex_shrink / total_flex_shrink);
845 item.main_size = (item.main_size + shrinkage).max(0.0);
846 }
847 }
848 }
849 Ok(())
850 }
851
852 fn calculate_justify_content_positions(
854 &self,
855 items: &[FlexItemData],
856 container_size: f32,
857 gap_size: f32,
858 justify_content: JustifyContent,
859 is_reverse: bool,
860 ) -> Vec<f32> {
861 let mut positions = Vec::with_capacity(items.len());
862
863 if items.is_empty() {
864 return positions;
865 }
866
867 let total_item_size: f32 = items.iter().map(|item| item.main_size).sum();
868 let total_gaps = gap_size * (items.len() as f32 - 1.0).max(0.0);
869 let remaining_space = container_size - total_item_size - total_gaps;
870
871 match justify_content {
872 JustifyContent::FlexStart => {
873 let mut current_pos = 0.0;
874 for item in items {
875 positions.push(current_pos);
876 current_pos += item.main_size + gap_size;
877 }
878 }
879 JustifyContent::FlexEnd => {
880 let mut current_pos = remaining_space;
881 for item in items {
882 positions.push(current_pos);
883 current_pos += item.main_size + gap_size;
884 }
885 }
886 JustifyContent::Center => {
887 let mut current_pos = remaining_space / 2.0;
888 for item in items {
889 positions.push(current_pos);
890 current_pos += item.main_size + gap_size;
891 }
892 }
893 JustifyContent::SpaceBetween => {
894 if items.len() == 1 {
895 positions.push(0.0);
896 } else {
897 let space_between = remaining_space / (items.len() as f32 - 1.0);
898 let mut current_pos = 0.0;
899 for item in items {
900 positions.push(current_pos);
901 current_pos += item.main_size + space_between;
902 }
903 }
904 }
905 JustifyContent::SpaceAround => {
906 let space_around = remaining_space / items.len() as f32;
907 let mut current_pos = space_around / 2.0;
908 for item in items {
909 positions.push(current_pos);
910 current_pos += item.main_size + space_around;
911 }
912 }
913 JustifyContent::SpaceEvenly => {
914 let space_evenly = remaining_space / (items.len() as f32 + 1.0);
915 let mut current_pos = space_evenly;
916 for item in items {
917 positions.push(current_pos);
918 current_pos += item.main_size + space_evenly;
919 }
920 }
921 }
922 if is_reverse {
923 let positions_len = positions.len();
925 for (i, pos) in positions.iter_mut().enumerate() {
926 let item_index = positions_len - 1 - i;
927 *pos = container_size - *pos - items[item_index].main_size;
928 }
929 positions.reverse();
930 }
931
932 positions
933 }
934
935 fn calculate_cross_axis_position(
937 &self,
938 child: &LayoutNode,
939 item_cross_size: f32,
940 container_cross_size: f32,
941 align_items: AlignItems,
942 ) -> f32 {
943 let alignment = child.style.align_self.unwrap_or(align_items);
945
946 match alignment {
947 AlignItems::FlexStart => 0.0,
948 AlignItems::FlexEnd => container_cross_size - item_cross_size,
949 AlignItems::Center => (container_cross_size - item_cross_size) / 2.0,
950 AlignItems::Stretch => 0.0, AlignItems::Baseline => {
952 0.0
955 }
956 }
957 }
958
959 fn layout_flex_multiline(
961 &mut self,
962 children: &mut [LayoutNode],
963 child_indices: &[usize],
964 container_size: Size,
965 parent_style: &LayoutStyle,
966 ) -> Result<(), LayoutError> {
967 let flex_direction = parent_style.flex_direction;
968 let is_row = matches!(
969 flex_direction,
970 FlexDirection::Row | FlexDirection::RowReverse
971 );
972 let is_reverse = matches!(parent_style.flex_wrap, FlexWrap::WrapReverse);
973
974 let main_axis_size = if is_row {
976 container_size.width
977 } else {
978 container_size.height
979 }; let lines = self.break_into_lines(children, child_indices, main_axis_size, is_row)?;
981
982 if lines.is_empty() {
983 return Ok(());
984 }
985
986 let cross_gap = if is_row {
988 parent_style.gap.row
989 } else {
990 parent_style.gap.column
991 };
992
993 let mut line_cross_sizes = Vec::with_capacity(lines.len());
995
996 for line_indices in &lines {
997 if line_indices.is_empty() {
998 line_cross_sizes.push(0.0);
999 continue;
1000 }
1001
1002 let line_cross_size = self.calculate_line_cross_size(children, line_indices, is_row);
1004 line_cross_sizes.push(line_cross_size);
1005 }
1006
1007 let mut current_cross_pos = 0.0;
1009 for (i, line_indices) in lines.iter().enumerate() {
1010 if line_indices.is_empty() {
1011 continue;
1012 }
1013
1014 let line_container = if is_row {
1016 Size::new(main_axis_size, container_size.height)
1017 } else {
1018 Size::new(container_size.width, main_axis_size)
1019 };
1020
1021 self.layout_flex_line(children, line_indices, line_container, parent_style)?;
1023 for &child_index in line_indices {
1025 let child = &mut children[child_index];
1026 self.layout_node(child, child.layout.rect.size)?;
1027 }
1028
1029 for &child_index in line_indices {
1031 let child = &mut children[child_index];
1032 if is_row {
1033 child.layout.rect.origin.y = current_cross_pos;
1035 } else {
1036 child.layout.rect.origin.x = current_cross_pos;
1038 }
1039 }
1040
1041 current_cross_pos += line_cross_sizes[i] + cross_gap;
1043 }
1044
1045 if is_reverse {
1047 let total_cross_size = current_cross_pos - cross_gap;
1048 for &child_index in child_indices {
1049 let child = &mut children[child_index];
1050 if is_row {
1051 child.layout.rect.origin.y =
1052 total_cross_size - child.layout.rect.origin.y - child.layout.rect.height();
1053 } else {
1054 child.layout.rect.origin.x =
1055 total_cross_size - child.layout.rect.origin.x - child.layout.rect.width();
1056 }
1057 }
1058 }
1059
1060 Ok(())
1061 }
1062
1063 fn break_into_lines(
1065 &self,
1066 children: &[LayoutNode],
1067 child_indices: &[usize],
1068 main_axis_size: f32,
1069 is_row: bool,
1070 ) -> Result<Vec<Vec<usize>>, LayoutError> {
1071 let mut lines = Vec::new();
1073 let mut current_line = Vec::new();
1074 let mut current_line_size = 0.0;
1075
1076 let gap_size = if is_row {
1078 children.first().map_or(0.0, |child| child.style.gap.column)
1079 } else {
1080 children.first().map_or(0.0, |child| child.style.gap.row)
1081 };
1082
1083 for &child_index in child_indices {
1084 let child = &children[child_index];
1085 let item_size = if is_row {
1086 child.style.width.resolve(main_axis_size)
1087 } else {
1088 child.style.height.resolve(main_axis_size)
1089 };
1090
1091 let gap_to_add = if current_line.is_empty() {
1093 0.0
1094 } else {
1095 gap_size
1096 };
1097
1098 if current_line.is_empty()
1100 || current_line_size + gap_to_add + item_size <= main_axis_size
1101 {
1102 current_line.push(child_index);
1103 current_line_size += item_size + gap_to_add;
1104 } else {
1105 lines.push(current_line);
1107 current_line = vec![child_index]; current_line_size = item_size;
1109 }
1110 }
1111
1112 if !current_line.is_empty() {
1114 lines.push(current_line);
1115 }
1116
1117 Ok(lines)
1118 }
1119 fn calculate_line_cross_size(
1121 &self,
1122 children: &[LayoutNode],
1123 line_indices: &[usize],
1124 is_row: bool,
1125 ) -> f32 {
1126 if line_indices.is_empty() {
1127 return 0.0;
1128 }
1129
1130 line_indices
1131 .iter()
1132 .map(|&index| {
1133 let child = &children[index];
1134 if is_row {
1135 match child.style.height {
1137 Dimension::Points(h) => h,
1138 Dimension::Percent(p) => p * child.layout.rect.height(),
1139 Dimension::Auto => child.layout.rect.height(),
1140 }
1141 } else {
1142 match child.style.width {
1144 Dimension::Points(w) => w,
1145 Dimension::Percent(p) => p * child.layout.rect.width(),
1146 Dimension::Auto => child.layout.rect.width(),
1147 }
1148 }
1149 })
1150 .fold(0.0, f32::max)
1151 }
1152}
1153
1154#[derive(Debug, Clone)]
1156struct FlexItemData {
1157 #[allow(dead_code)]
1158 index: usize,
1159 #[allow(dead_code)]
1160 basis_size: f32,
1161 main_size: f32,
1162 cross_size: f32,
1163 flex_grow: f32,
1164 flex_shrink: f32,
1165}
1166
1167#[derive(Debug, Clone, Copy, PartialEq, Default)]
1169pub struct Gap {
1170 pub row: f32,
1171 pub column: f32,
1172}
1173
1174impl Gap {
1175 pub fn new(row: f32, column: f32) -> Self {
1176 Self { row, column }
1177 }
1178
1179 pub fn uniform(value: f32) -> Self {
1180 Self::new(value, value)
1181 }
1182}
1183
1184impl Default for LayoutEngine {
1185 fn default() -> Self {
1186 Self::new()
1187 }
1188}
1189
1190#[derive(Debug, thiserror::Error)]
1192pub enum LayoutError {
1193 #[error("Invalid layout constraint: {0}")]
1194 InvalidConstraint(String),
1195
1196 #[error("Layout calculation failed: {0}")]
1197 CalculationFailed(String),
1198
1199 #[error("Circular dependency detected in layout tree")]
1200 CircularDependency,
1201
1202 #[error("Node not found: {0:?}")]
1203 NodeNotFound(ComponentId),
1204}
1205
1206impl fmt::Display for LayoutStats {
1207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1208 write!(
1209 f,
1210 "Layout Stats: {} calculations, {}μs total time, {} nodes, {}/{} cache hit/miss",
1211 self.layout_calculations,
1212 self.layout_time_us,
1213 self.node_count,
1214 self.cache_hits,
1215 self.cache_misses
1216 )
1217 }
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use super::*;
1223
1224 #[test]
1225 fn test_point_creation() {
1226 let point = Point::new(10.0, 20.0);
1227 assert_eq!(point.x, 10.0);
1228 assert_eq!(point.y, 20.0);
1229
1230 let zero_point = Point::zero();
1231 assert_eq!(zero_point.x, 0.0);
1232 assert_eq!(zero_point.y, 0.0);
1233 }
1234
1235 #[test]
1236 fn test_size_operations() {
1237 let size = Size::new(100.0, 200.0);
1238 assert_eq!(size.width, 100.0);
1239 assert_eq!(size.height, 200.0);
1240 assert_eq!(size.area(), 20000.0);
1241
1242 let zero_size = Size::zero();
1243 assert_eq!(zero_size.area(), 0.0);
1244 }
1245
1246 #[test]
1247 fn test_rect_operations() {
1248 let rect = Rect::new(10.0, 20.0, 100.0, 200.0);
1249 assert_eq!(rect.x(), 10.0);
1250 assert_eq!(rect.y(), 20.0);
1251 assert_eq!(rect.width(), 100.0);
1252 assert_eq!(rect.height(), 200.0);
1253 assert_eq!(rect.max_x(), 110.0);
1254 assert_eq!(rect.max_y(), 220.0);
1255
1256 let point_inside = Point::new(50.0, 100.0);
1257 let point_outside = Point::new(150.0, 100.0);
1258 assert!(rect.contains_point(point_inside));
1259 assert!(!rect.contains_point(point_outside));
1260 }
1261
1262 #[test]
1263 fn test_dimension_resolve() {
1264 let auto_dim = Dimension::Auto;
1265 let points_dim = Dimension::Points(100.0);
1266 let percent_dim = Dimension::Percent(50.0);
1267
1268 assert_eq!(auto_dim.resolve(200.0), 0.0);
1269 assert_eq!(points_dim.resolve(200.0), 100.0);
1270 assert_eq!(percent_dim.resolve(200.0), 100.0);
1271 }
1272
1273 #[test]
1274 fn test_edge_values() {
1275 let edges = EdgeValues::new(10.0, 20.0, 30.0, 40.0);
1276 assert_eq!(edges.horizontal(), 60.0);
1277 assert_eq!(edges.vertical(), 40.0);
1278
1279 let uniform = EdgeValues::uniform(15.0);
1280 assert_eq!(uniform.top, 15.0);
1281 assert_eq!(uniform.right, 15.0);
1282 assert_eq!(uniform.bottom, 15.0);
1283 assert_eq!(uniform.left, 15.0);
1284 }
1285
1286 #[test]
1287 fn test_layout_node_creation() {
1288 let id = ComponentId::new();
1289 let style = LayoutStyle::default();
1290 let node = LayoutNode::new(id, style);
1291
1292 assert_eq!(node.id, id);
1293 assert!(node.children.is_empty());
1294 assert!(node.layout.is_dirty);
1295 }
1296
1297 #[test]
1298 fn test_layout_node_children() {
1299 let parent_id = ComponentId::new();
1300 let child_id = ComponentId::new();
1301
1302 let mut parent = LayoutNode::new(parent_id, LayoutStyle::default());
1303 let child = LayoutNode::new(child_id, LayoutStyle::default());
1304
1305 parent.add_child(child);
1306 assert_eq!(parent.children.len(), 1);
1307 assert_eq!(parent.children[0].parent_id, Some(parent_id));
1308
1309 let removed_child = parent.remove_child(child_id);
1310 assert!(removed_child.is_some());
1311 assert_eq!(parent.children.len(), 0);
1312 assert_eq!(removed_child.unwrap().parent_id, None);
1313 }
1314
1315 #[test]
1316 fn test_layout_engine_creation() {
1317 let engine = LayoutEngine::new();
1318 assert_eq!(engine.stats.layout_calculations, 0);
1319 assert_eq!(engine.stats.cache_hits, 0);
1320 assert_eq!(engine.stats.cache_misses, 0);
1321 }
1322
1323 #[test]
1324 fn test_simple_layout_calculation() {
1325 let mut engine = LayoutEngine::new();
1326 let id = ComponentId::new();
1327 let style = LayoutStyle {
1328 width: Dimension::Points(100.0),
1329 height: Dimension::Points(200.0),
1330 ..Default::default()
1331 };
1332
1333 let mut root = LayoutNode::new(id, style);
1334 let container_size = Size::new(400.0, 600.0);
1335
1336 let result = engine.calculate_layout(&mut root, container_size);
1337 assert!(result.is_ok());
1338
1339 assert_eq!(root.layout.rect.width(), 100.0);
1340 assert_eq!(root.layout.rect.height(), 200.0);
1341 assert!(!root.layout.is_dirty);
1342 }
1343
1344 #[test]
1345 fn test_flexbox_row_layout() {
1346 let mut engine = LayoutEngine::new();
1347
1348 let parent_id = ComponentId::new();
1350 let parent_style = LayoutStyle {
1351 flex_direction: FlexDirection::Row,
1352 width: Dimension::Points(300.0),
1353 height: Dimension::Points(100.0),
1354 ..Default::default()
1355 };
1356 let mut parent = LayoutNode::new(parent_id, parent_style);
1357
1358 let child1_id = ComponentId::new();
1360 let child1_style = LayoutStyle {
1361 flex_grow: 1.0,
1362 ..Default::default()
1363 };
1364 let child1 = LayoutNode::new(child1_id, child1_style);
1365
1366 let child2_id = ComponentId::new();
1367 let child2_style = LayoutStyle {
1368 flex_grow: 2.0,
1369 ..Default::default()
1370 };
1371 let child2 = LayoutNode::new(child2_id, child2_style);
1372
1373 parent.add_child(child1);
1374 parent.add_child(child2);
1375
1376 let container_size = Size::new(400.0, 200.0);
1377 let result = engine.calculate_layout(&mut parent, container_size);
1378 assert!(result.is_ok());
1379
1380 assert_eq!(parent.children.len(), 2);
1382
1383 assert_eq!(engine.stats.layout_calculations, 1);
1385 }
1386
1387 #[test]
1388 fn test_layout_caching() {
1389 let mut engine = LayoutEngine::new();
1390 let id = ComponentId::new();
1391 let style = LayoutStyle::default();
1392 let mut root = LayoutNode::new(id, style);
1393 let container_size = Size::new(100.0, 100.0);
1394
1395 let result = engine.calculate_layout(&mut root, container_size);
1397 assert!(result.is_ok());
1398 assert_eq!(engine.stats.cache_misses, 1);
1399
1400 root.layout.is_dirty = false;
1402 let result = engine.calculate_layout(&mut root, container_size);
1403 assert!(result.is_ok());
1404
1405 assert!(engine.stats.cache_hits > 0 || engine.stats.cache_misses > 1);
1407 }
1408
1409 #[test]
1410 fn test_layout_stats_display() {
1411 let stats = LayoutStats {
1412 layout_calculations: 5,
1413 layout_time_us: 1000,
1414 node_count: 10,
1415 cache_hits: 3,
1416 cache_misses: 7,
1417 };
1418 let display_string = format!("{stats}");
1419 assert!(display_string.contains("5 calculations"));
1420 assert!(display_string.contains("1000μs"));
1421 assert!(display_string.contains("10 nodes"));
1422 assert!(display_string.contains("3/7 cache"));
1423 }
1424 #[test]
1425 fn test_main_cross_axis_calculations() {
1426 let id = ComponentId::new();
1427
1428 let style = LayoutStyle {
1430 flex_direction: FlexDirection::Row,
1431 ..Default::default()
1432 };
1433 let mut node = LayoutNode::new(id, style);
1434 node.layout.rect = Rect::new(0.0, 0.0, 100.0, 50.0);
1435
1436 assert_eq!(node.main_axis_size(), 100.0); assert_eq!(node.cross_axis_size(), 50.0); node.style.flex_direction = FlexDirection::Column;
1441 assert_eq!(node.main_axis_size(), 50.0); assert_eq!(node.cross_axis_size(), 100.0); }
1444
1445 #[test]
1446 fn test_justify_content_flex_start() {
1447 let mut engine = LayoutEngine::new();
1448 let parent_id = ComponentId::new();
1449 let parent_style = LayoutStyle {
1450 flex_direction: FlexDirection::Row,
1451 justify_content: JustifyContent::FlexStart,
1452 width: Dimension::Points(300.0),
1453 height: Dimension::Points(100.0),
1454 ..Default::default()
1455 };
1456 let mut parent = LayoutNode::new(parent_id, parent_style); for _ in 0..3 {
1458 let child_id = ComponentId::new();
1459 let child_style = LayoutStyle {
1460 width: Dimension::Points(50.0),
1461 height: Dimension::Points(50.0),
1462 ..Default::default()
1463 };
1464 let child = LayoutNode::new(child_id, child_style);
1465 parent.add_child(child);
1466 }
1467
1468 let container_size = Size::new(400.0, 200.0);
1469 let result = engine.calculate_layout(&mut parent, container_size);
1470 assert!(result.is_ok());
1471
1472 assert_eq!(parent.children[0].layout.rect.x(), 0.0);
1474 assert_eq!(parent.children[1].layout.rect.x(), 50.0);
1475 assert_eq!(parent.children[2].layout.rect.x(), 100.0);
1476 }
1477
1478 #[test]
1479 fn test_justify_content_center() {
1480 let mut engine = LayoutEngine::new();
1481 let parent_id = ComponentId::new();
1482 let parent_style = LayoutStyle {
1483 flex_direction: FlexDirection::Row,
1484 justify_content: JustifyContent::Center,
1485 width: Dimension::Points(300.0),
1486 height: Dimension::Points(100.0),
1487 ..Default::default()
1488 };
1489 let mut parent = LayoutNode::new(parent_id, parent_style);
1490
1491 let child_id = ComponentId::new();
1493 let child_style = LayoutStyle {
1494 width: Dimension::Points(100.0),
1495 height: Dimension::Points(50.0),
1496 ..Default::default()
1497 };
1498 let child = LayoutNode::new(child_id, child_style);
1499 parent.add_child(child);
1500
1501 let container_size = Size::new(400.0, 200.0);
1502 let result = engine.calculate_layout(&mut parent, container_size);
1503 assert!(result.is_ok());
1504
1505 assert_eq!(parent.children[0].layout.rect.x(), 100.0);
1507 }
1508
1509 #[test]
1510 fn test_justify_content_space_between() {
1511 let mut engine = LayoutEngine::new();
1512 let parent_id = ComponentId::new();
1513 let parent_style = LayoutStyle {
1514 flex_direction: FlexDirection::Row,
1515 justify_content: JustifyContent::SpaceBetween,
1516 width: Dimension::Points(300.0),
1517 height: Dimension::Points(100.0),
1518 ..Default::default()
1519 };
1520 let mut parent = LayoutNode::new(parent_id, parent_style);
1521
1522 for _ in 0..2 {
1524 let child_id = ComponentId::new();
1525 let child_style = LayoutStyle {
1526 width: Dimension::Points(50.0),
1527 height: Dimension::Points(50.0),
1528 ..Default::default()
1529 };
1530 let child = LayoutNode::new(child_id, child_style);
1531 parent.add_child(child);
1532 }
1533
1534 let container_size = Size::new(400.0, 200.0);
1535 let result = engine.calculate_layout(&mut parent, container_size);
1536 assert!(result.is_ok());
1537
1538 assert_eq!(parent.children[0].layout.rect.x(), 0.0);
1540 assert_eq!(parent.children[1].layout.rect.x(), 250.0); }
1542
1543 #[test]
1544 fn test_align_items_center() {
1545 let mut engine = LayoutEngine::new();
1546 let parent_id = ComponentId::new();
1547 let parent_style = LayoutStyle {
1548 flex_direction: FlexDirection::Row,
1549 align_items: AlignItems::Center,
1550 width: Dimension::Points(300.0),
1551 height: Dimension::Points(100.0),
1552 ..Default::default()
1553 };
1554 let mut parent = LayoutNode::new(parent_id, parent_style);
1555
1556 let child_id = ComponentId::new();
1558 let child_style = LayoutStyle {
1559 width: Dimension::Points(50.0),
1560 height: Dimension::Points(30.0),
1561 ..Default::default()
1562 };
1563 let child = LayoutNode::new(child_id, child_style);
1564 parent.add_child(child);
1565
1566 let container_size = Size::new(400.0, 200.0);
1567 let result = engine.calculate_layout(&mut parent, container_size);
1568 assert!(result.is_ok());
1569
1570 assert_eq!(parent.children[0].layout.rect.y(), 35.0);
1572 }
1573
1574 #[test]
1575 fn test_align_items_flex_end() {
1576 let mut engine = LayoutEngine::new();
1577 let parent_id = ComponentId::new();
1578 let parent_style = LayoutStyle {
1579 flex_direction: FlexDirection::Row,
1580 align_items: AlignItems::FlexEnd,
1581 width: Dimension::Points(300.0),
1582 height: Dimension::Points(100.0),
1583 ..Default::default()
1584 };
1585 let mut parent = LayoutNode::new(parent_id, parent_style);
1586
1587 let child_id = ComponentId::new();
1589 let child_style = LayoutStyle {
1590 width: Dimension::Points(50.0),
1591 height: Dimension::Points(30.0),
1592 ..Default::default()
1593 };
1594 let child = LayoutNode::new(child_id, child_style);
1595 parent.add_child(child);
1596
1597 let container_size = Size::new(400.0, 200.0);
1598 let result = engine.calculate_layout(&mut parent, container_size);
1599 assert!(result.is_ok());
1600
1601 assert_eq!(parent.children[0].layout.rect.y(), 70.0);
1603 }
1604
1605 #[test]
1606 fn test_flex_grow_distribution() {
1607 let mut engine = LayoutEngine::new();
1608 let parent_id = ComponentId::new();
1609 let parent_style = LayoutStyle {
1610 flex_direction: FlexDirection::Row,
1611 width: Dimension::Points(300.0),
1612 height: Dimension::Points(100.0),
1613 ..Default::default()
1614 };
1615 let mut parent = LayoutNode::new(parent_id, parent_style);
1616
1617 let child1_style = LayoutStyle {
1619 flex_grow: 1.0,
1620 flex_basis: Dimension::Points(50.0),
1621 ..Default::default()
1622 };
1623 let child1 = LayoutNode::new(ComponentId::new(), child1_style);
1624 parent.add_child(child1);
1625
1626 let child2_style = LayoutStyle {
1627 flex_grow: 2.0,
1628 flex_basis: Dimension::Points(50.0),
1629 ..Default::default()
1630 };
1631 let child2 = LayoutNode::new(ComponentId::new(), child2_style);
1632 parent.add_child(child2);
1633
1634 let container_size = Size::new(400.0, 200.0);
1635 let result = engine.calculate_layout(&mut parent, container_size);
1636 assert!(result.is_ok());
1637
1638 assert!((parent.children[0].layout.rect.width() - 116.67).abs() < 0.1);
1642 assert!((parent.children[1].layout.rect.width() - 183.33).abs() < 0.1);
1643 }
1644
1645 #[test]
1646 fn test_gap_spacing() {
1647 let mut engine = LayoutEngine::new();
1648 let parent_id = ComponentId::new();
1649 let parent_style = LayoutStyle {
1650 flex_direction: FlexDirection::Row,
1651 width: Dimension::Points(300.0),
1652 height: Dimension::Points(100.0),
1653 gap: Gap::uniform(10.0),
1654 ..Default::default()
1655 };
1656 let mut parent = LayoutNode::new(parent_id, parent_style);
1657
1658 for _ in 0..2 {
1660 let child_style = LayoutStyle {
1661 width: Dimension::Points(50.0),
1662 height: Dimension::Points(50.0),
1663 ..Default::default()
1664 };
1665 let child = LayoutNode::new(ComponentId::new(), child_style);
1666 parent.add_child(child);
1667 }
1668
1669 let container_size = Size::new(400.0, 200.0);
1670 let result = engine.calculate_layout(&mut parent, container_size);
1671 assert!(result.is_ok());
1672
1673 assert_eq!(parent.children[0].layout.rect.x(), 0.0);
1675 assert_eq!(parent.children[1].layout.rect.x(), 60.0); }
1677
1678 #[test]
1679 fn test_column_direction() {
1680 let mut engine = LayoutEngine::new();
1681 let parent_id = ComponentId::new();
1682 let parent_style = LayoutStyle {
1683 flex_direction: FlexDirection::Column,
1684 width: Dimension::Points(100.0),
1685 height: Dimension::Points(300.0),
1686 ..Default::default()
1687 };
1688 let mut parent = LayoutNode::new(parent_id, parent_style);
1689
1690 for i in 0..2 {
1692 let child_style = LayoutStyle {
1693 flex_grow: (i + 1) as f32,
1694 ..Default::default()
1695 };
1696 let child = LayoutNode::new(ComponentId::new(), child_style);
1697 parent.add_child(child);
1698 }
1699
1700 let container_size = Size::new(200.0, 400.0);
1701 let result = engine.calculate_layout(&mut parent, container_size);
1702 assert!(result.is_ok());
1703
1704 assert_eq!(parent.children[0].layout.rect.y(), 0.0);
1706 assert!(parent.children[1].layout.rect.y() > parent.children[0].layout.rect.height());
1707 }
1708
1709 #[test]
1710 fn test_flex_wrap_basic() {
1711 let mut engine = LayoutEngine::new();
1712 let parent_id = ComponentId::new();
1713 let parent_style = LayoutStyle {
1714 flex_direction: FlexDirection::Row,
1715 flex_wrap: FlexWrap::Wrap,
1716 width: Dimension::Points(200.0),
1717 height: Dimension::Points(200.0),
1718 ..Default::default()
1719 };
1720 let mut parent = LayoutNode::new(parent_id, parent_style);
1721
1722 for _ in 0..3 {
1724 let child_style = LayoutStyle {
1725 width: Dimension::Points(100.0),
1726 height: Dimension::Points(50.0),
1727 ..Default::default()
1728 };
1729 let child = LayoutNode::new(ComponentId::new(), child_style);
1730 parent.add_child(child);
1731 }
1732 let container_size = Size::new(300.0, 400.0);
1733 let result = engine.calculate_layout(&mut parent, container_size);
1734 assert!(result.is_ok());
1735
1736 assert_eq!(
1738 parent.children[0].layout.rect.y(),
1739 parent.children[1].layout.rect.y()
1740 );
1741 assert!(parent.children[2].layout.rect.y() > parent.children[0].layout.rect.y());
1742 }
1743
1744 #[test]
1745 fn test_gap_structure() {
1746 let gap = Gap::new(10.0, 20.0);
1747 assert_eq!(gap.row, 10.0);
1748 assert_eq!(gap.column, 20.0);
1749
1750 let uniform_gap = Gap::uniform(15.0);
1751 assert_eq!(uniform_gap.row, 15.0);
1752 assert_eq!(uniform_gap.column, 15.0);
1753
1754 let default_gap = Gap::default();
1755 assert_eq!(default_gap.row, 0.0);
1756 assert_eq!(default_gap.column, 0.0);
1757 }
1758}