|
742 | 742 | }); |
743 | 743 | }, |
744 | 744 |
|
745 | | - align_grid: function(gridGap) { |
| 745 | + align_grid: function(gridGap) { |
746 | 746 | var sel = this.getSelected(), |
747 | | - top_left = this.get_top_left_panel(sel), |
748 | | - top_x = top_left.get('x'), |
749 | | - top_y = top_left.get('y'), |
| 747 | + top = this.get_top_panel(sel), |
| 748 | + left = this.get_left_panel(sel), |
| 749 | + right = this.get_right_panel(sel), |
| 750 | + bottom = this.get_bottom_panel(sel), |
| 751 | + left_x = left.get('x'), |
| 752 | + right_x = right.get('x') + right.get('width'), |
| 753 | + top_y = top.get('y'), |
| 754 | + bottom_y = bottom.get('y') + bottom.get('height'), |
750 | 755 | grid = [], |
751 | | - row = [top_left], |
752 | | - next_panel = top_left; |
753 | | - |
754 | | - // populate the grid, getting neighbouring panel each time |
755 | | - while (next_panel) { |
756 | | - c = next_panel.get_centre(); |
757 | | - next_panel = this.get_panel_at(c.x + next_panel.get('width'), c.y, sel); |
| 756 | + row = [], |
| 757 | + next_panel, |
| 758 | + c = {'x': left_x + left.get('width')/2, 'y': top_y + top.get('height')/2}; |
| 759 | + |
| 760 | + // loop over the rows, starting by the top panel row |
| 761 | + while(c.y < bottom_y){ |
| 762 | + row = [] |
| 763 | + // loop over the columns, starting by the left panel column |
| 764 | + while(c.x < right_x){ |
| 765 | + next_panel = this.get_panel_at(c.x , c.y, sel); |
| 766 | + |
| 767 | + // if a panel doesn't exist at the current position c |
| 768 | + // just go to the next position until it reaches the selection boundaries |
| 769 | + if (next_panel) { |
| 770 | + row.push(next_panel); |
| 771 | + c = next_panel.get_centre(); |
| 772 | + }else{ |
| 773 | + next_panel = next_panel == undefined ? left : next_panel |
| 774 | + } |
| 775 | + c = {'x': c.x + next_panel.get('width'), 'y': c.y} |
| 776 | + } |
758 | 777 |
|
759 | | - // if next_panel is not found, reached end of row. Try start new row... |
760 | | - if (typeof next_panel == 'undefined') { |
| 778 | + // check that there is effecitvely a panel in the current row |
| 779 | + if(row.length == 0){ |
| 780 | + c = {'x': left_x + left.get('width')/2, 'y': c.y + left.get('height')} |
| 781 | + }else{ |
| 782 | + c = {'x': left_x + left.get('width')/2, 'y': c.y + row[0].get('height')} |
761 | 783 | grid.push(row); |
762 | | - // next_panel is below the first of the current row |
763 | | - c = row[0].get_centre(); |
764 | | - next_panel = this.get_panel_at(c.x, c.y + row[0].get('height'), sel); |
765 | | - row = []; |
766 | | - } |
767 | | - if (next_panel) { |
768 | | - row.push(next_panel); |
769 | | - } |
| 784 | + } |
770 | 785 | } |
771 | 786 |
|
772 | | - var spacer = top_left.get('width')/20; |
| 787 | + // get the row id of the most left panel |
| 788 | + var left_panel_row = 0; |
| 789 | + grid.forEach((row, i) => { |
| 790 | + if (row.some(panel => panel.cid === left.cid)) { |
| 791 | + left_panel_row = i; |
| 792 | + } |
| 793 | + }); |
| 794 | + |
| 795 | + // define the spacer between images |
| 796 | + var spacer = left.get('width')/20; |
773 | 797 | if (!isNaN(parseFloat(gridGap))) { |
774 | 798 | spacer = parseFloat(gridGap); |
775 | 799 | } |
776 | | - var new_x = top_x, |
777 | | - new_y = top_y, |
778 | | - max_h = 0; |
779 | | - for (var r=0; r<grid.length; r++) { |
780 | | - row = grid[r]; |
| 800 | + |
| 801 | + var new_x = left_x, |
| 802 | + new_y = left.get('y'), |
| 803 | + max_h = 0, |
| 804 | + ref_row = grid[left_panel_row], |
| 805 | + last_panel_width = 0, |
| 806 | + reference_grid = {}; |
| 807 | + |
| 808 | + // start aligning the row with the most left panel |
| 809 | + // This row is considered as a reference to align |
| 810 | + // the rest of the panels |
| 811 | + var global_index = 0 |
| 812 | + for (var c = 0; c < ref_row.length; c++) { |
| 813 | + let panel = ref_row[c]; |
| 814 | + var current_x = panel.get('x') |
| 815 | + var gap = Math.floor(Math.abs(current_x - new_x) / last_panel_width) |
| 816 | + for(var i = 0; i < gap; i++){ |
| 817 | + reference_grid[global_index] = new_x |
| 818 | + new_x = new_x + spacer + last_panel_width; |
| 819 | + global_index++ |
| 820 | + } |
| 821 | + panel.save({'x':new_x, 'y':new_y}); |
| 822 | + reference_grid[global_index] = new_x |
| 823 | + max_h = Math.max(max_h, panel.get('height')); |
| 824 | + new_x = new_x + spacer + panel.get('width'); |
| 825 | + last_panel_width = panel.get('width') |
| 826 | + global_index++ |
| 827 | + } |
| 828 | + |
| 829 | + // set the row position (i.e. y coordinate) of each row |
| 830 | + var ref_y_offset = max_h |
| 831 | + var rows_position = {} |
| 832 | + max_h = 0 |
| 833 | + // for rows above the reference row |
| 834 | + for (var r = left_panel_row - 1; r >= 0; r--) { |
| 835 | + var row = grid[r]; |
| 836 | + for (var c = 0; c < row.length; c++) { |
| 837 | + max_h = Math.max(max_h, row[c].get('height')); |
| 838 | + } |
| 839 | + new_y = new_y - spacer - max_h; |
| 840 | + rows_position[r] = new_y |
| 841 | + } |
| 842 | + max_h = ref_y_offset |
| 843 | + new_y = left.get('y') |
| 844 | + // for rows below the reference row |
| 845 | + for (var r = left_panel_row + 1; r < grid.length; r++) { |
| 846 | + var row = grid[r]; |
| 847 | + new_y = new_y + spacer + max_h; |
| 848 | + rows_position[r] = new_y |
| 849 | + for (var c = 0; c < row.length; c++) { |
| 850 | + max_h = Math.max(max_h, row[c].get('height')); |
| 851 | + } |
| 852 | + } |
| 853 | + |
| 854 | + // update position of panels |
| 855 | + for (var [r, y] of Object.entries(rows_position)){ |
| 856 | + var row = grid[r]; |
| 857 | + var last_column_id = -1 |
781 | 858 | for (var c=0; c<row.length; c++) { |
782 | 859 | let panel = row[c]; |
783 | | - panel.save({'x':new_x, 'y':new_y}); |
784 | | - max_h = Math.max(max_h, panel.get('height')); |
| 860 | + var closest_column = this.get_closest_column(panel, reference_grid, last_panel_width) |
| 861 | + |
| 862 | + if(closest_column >= 0){ |
| 863 | + // update closest_column id to take into account spare panel positions |
| 864 | + if(last_column_id == closest_column){ |
| 865 | + closest_column = last_column_id + 1 |
| 866 | + if(closest_column == reference_grid.length){ |
| 867 | + new_x = reference_grid[closest_column-1] + last_panel_width + spacer |
| 868 | + }else{ |
| 869 | + new_x = reference_grid[closest_column] |
| 870 | + } |
| 871 | + }else{ |
| 872 | + new_x = reference_grid[closest_column] |
| 873 | + } |
| 874 | + }else{ |
| 875 | + // update closest_column id to take into account spare panel positions |
| 876 | + if(last_column_id == reference_grid.length - 1 - closest_column){ |
| 877 | + closest_column-- |
| 878 | + } |
| 879 | + var lastRefColumn = Object.keys(reference_grid).length - 1 |
| 880 | + new_x = reference_grid[lastRefColumn] -closest_column*(last_panel_width + spacer) |
| 881 | + } |
| 882 | + last_column_id++ |
| 883 | + panel.save({'x':new_x, 'y':y}); |
785 | 884 | new_x = new_x + spacer + panel.get('width'); |
786 | 885 | } |
787 | | - new_y = new_y + spacer + max_h; |
788 | | - new_x = top_x; |
789 | 886 | } |
790 | 887 | }, |
791 | 888 |
|
| 889 | + get_closest_column: function(panel, reference_row, last_ref_panel_width){ |
| 890 | + // look at the reference row (i.e. the one with the most left panel) |
| 891 | + // and find the closest column to the current panel |
| 892 | + var closest_col = 0; |
| 893 | + var min_x_distance = Number.MAX_VALUE |
| 894 | + for (var [col_id, col_x] of Object.entries(reference_row)){ |
| 895 | + var current_distance = Math.abs(col_x - panel.get('x')) |
| 896 | + if(current_distance < min_x_distance){ |
| 897 | + closest_col = col_id |
| 898 | + min_x_distance = current_distance |
| 899 | + } |
| 900 | + } |
| 901 | + |
| 902 | + // if the panel is located far away from the last reference column, |
| 903 | + // return the number of extra columns where to put the panel as a negative number |
| 904 | + if(closest_col == Object.keys(reference_row).length - 1 && min_x_distance > last_ref_panel_width){ |
| 905 | + closest_col = - Math.floor(min_x_distance/last_ref_panel_width) |
| 906 | + } |
| 907 | + return closest_col |
| 908 | + }, |
| 909 | + |
792 | 910 | get_panel_at: function(x, y, panels) { |
793 | 911 | return panels.find(function(p) { |
794 | 912 | return ((p.get('x') < x && (p.get('x')+p.get('width')) > x) && |
|
803 | 921 | return p; |
804 | 922 | } else { |
805 | 923 | return top_left; |
| 924 | + } |
| 925 | + }); |
| 926 | + }, |
| 927 | + |
| 928 | + get_top_panel: function(panels) { |
| 929 | + // top panel is one where y is least |
| 930 | + return panels.reduce(function(top, p){ |
| 931 | + if (p.get('y') < top.get('y')) { |
| 932 | + return p; |
| 933 | + } else { |
| 934 | + return top; |
| 935 | + } |
| 936 | + }); |
| 937 | + }, |
| 938 | + |
| 939 | + get_bottom_panel: function(panels) { |
| 940 | + // bottom panel is one where y is greater |
| 941 | + return panels.reduce(function(bottom, p){ |
| 942 | + if (p.get('y') > bottom.get('y')) { |
| 943 | + return p; |
| 944 | + } else { |
| 945 | + return bottom; |
| 946 | + } |
| 947 | + }); |
| 948 | + }, |
| 949 | + |
| 950 | + get_left_panel: function(panels) { |
| 951 | + // left panel is one where x is least |
| 952 | + return panels.reduce(function(left, p){ |
| 953 | + if (p.get('x') < left.get('x')) { |
| 954 | + return p; |
| 955 | + } else { |
| 956 | + return left; |
| 957 | + } |
| 958 | + }); |
| 959 | + }, |
| 960 | + |
| 961 | + get_right_panel: function(panels) { |
| 962 | + // right panel is one where x is greater |
| 963 | + return panels.reduce(function(right, p){ |
| 964 | + if (p.get('x') > right.get('x')) { |
| 965 | + return p; |
| 966 | + } else { |
| 967 | + return right; |
806 | 968 | } |
807 | 969 | }); |
808 | 970 | }, |
|
0 commit comments