From 9842ff8f1bfc8da6daf34d91a420cd9ed8dc38dd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 14:11:59 +0200 Subject: [PATCH] WIP: new implementation of distribute algorithm - the previous one wasn't really smart. --- src/edt/edt/DistributeOptionsDialog.ui | 629 +++++++++++++---------- src/edt/edt/edtDialogs.cc | 6 +- src/edt/edt/edtDistribute.h | 220 +++++++- src/edt/edt/edtMainService.cc | 13 +- src/edt/unit_tests/edtDistributeTests.cc | 71 ++- 5 files changed, 633 insertions(+), 306 deletions(-) diff --git a/src/edt/edt/DistributeOptionsDialog.ui b/src/edt/edt/DistributeOptionsDialog.ui index 0d2bf52e7..74929ebe1 100644 --- a/src/edt/edt/DistributeOptionsDialog.ui +++ b/src/edt/edt/DistributeOptionsDialog.ui @@ -6,7 +6,7 @@ 0 0 - 716 + 689 574 @@ -23,147 +23,17 @@ true - - + + - Alignment + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true - - - - right - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - center - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 243 - 20 - - - - - - - - - - - - :/align_left.png:/align_left.png - - - - 32 - 32 - - - - - - - - - - - - :/align_right.png:/align_right.png - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - - - - - - - - :/align_hcenter.png:/align_hcenter.png - - - - 32 - 32 - - - - - - - - left - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + QFrame::NoFrame @@ -264,16 +134,6 @@ - - - - The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. - - - true - - - @@ -282,7 +142,7 @@ 0 - 200 + 0 @@ -291,87 +151,8 @@ true - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - top - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - :/align_vcenter.png:/align_vcenter.png - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - 243 - 34 - - - - - - - - Qt::Horizontal - - - - + + QFrame::NoFrame @@ -472,44 +253,7 @@ - - - - center - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - bottom - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - :/align_bottom.png:/align_bottom.png - - - - 32 - 32 - - - - - + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. @@ -519,7 +263,227 @@ - + + + + + + + Horizonal alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_left.png:/align_left.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_hcenter.png:/align_hcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_right.png:/align_right.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + left + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + right + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Vertical alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + @@ -536,13 +500,132 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_vcenter.png:/align_vcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_bottom.png:/align_bottom.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + top + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + bottom + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + - Layers for distribution of instances + For the computation of cell instance bounding boxes ... @@ -590,12 +673,6 @@ all_layers_rb visible_layers_rb - h_left_rb - h_center_rb - h_right_rb - v_top_rb - v_center_rb - v_bottom_rb buttonBox diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 63ac14fdc..21a0f6393 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -343,10 +343,10 @@ DistributeOptionsDialog::~DistributeOptionsDialog () } bool -DistributeOptionsDialog::exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) +DistributeOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) { - QRadioButton *hmode_buttons [] = { (QRadioButton *) 0, this->h_left_rb, this->h_center_rb, this->h_right_rb }; - QRadioButton *vmode_buttons [] = { (QRadioButton *) 0, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; + QRadioButton *hmode_buttons [] = { this->h_none_rb, this->h_left_rb, this->h_center_rb, this->h_right_rb }; + QRadioButton *vmode_buttons [] = { this->v_none_rb, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb }; this->h_distribute->setChecked (hdistribute); diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h index 89b725dec..8f40dd024 100644 --- a/src/edt/edt/edtDistribute.h +++ b/src/edt/edt/edtDistribute.h @@ -150,6 +150,7 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c if (first) { min = b1; max = b2; + first = false; } else { min = std::min (min, b1); max = std::max (max, b1); @@ -163,9 +164,19 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c // Determines the next column/row's position as the minimum position which is compatible with // the space constraint. + coord_type prev_bin_end = 0; + bool prev_bin_end_set = false; + for (std::vector >::const_iterator b = bins.begin (); b != bins.end (); ++b) { coord_type min_pos = 1; + coord_type bin_end = prev_bin_end; + bool bin_end_set = prev_bin_end_set; + + coord_type max_limit = 0; + for (typename tl::interval_map::const_iterator j = limits.begin (); j != limits.end (); ++j) { + max_limit = std::max (max_limit, j->second); + } for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { @@ -176,13 +187,32 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c coord_type start = box_position (box, -1); coord_type ref_pos = box_position (box, ref); + coord_type end_pos = box_position (box, 1); + + bin_end = bin_end_set ? std::max (end_pos, bin_end) : end_pos; + bin_end_set = true; + + if (prev_bin_end_set && db::coord_traits::less (start, prev_bin_end)) { + + // for boxes overlapping into the previous bin try to shift them into the previous bin + for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { + if (db::coord_traits::less (b1, j->first.second)) { + min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); + } + } + + } else { + + // otherwise separate the bins + min_pos = std::max (min_pos, max_limit + space + (ref_pos - start)); - for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { - min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); } } + prev_bin_end = bin_end; + prev_bin_end_set = bin_end_set; + if (pitch > 0) { min_pos = db::coord_traits::rounded (ceil (double (min_pos) / double (pitch) - 1e-10) * double (pitch)); } @@ -194,16 +224,29 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c coord_type b1 = box_position (box, -1); coord_type b2 = box_position (box, 1); + coord_type start = box_position (box, -1); coord_type ref_pos = box_position (box, ref); coord_type end_pos = box_position (box, 1); - if (horizontally) { - box.move (db::vector (min_pos - ref_pos, 0)); - } else { - box.move (db::vector (0, min_pos - ref_pos)); + // because multiple objects may fall into one bin we need to look up again: + coord_type pos = min_pos; + for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { + if (db::coord_traits::less (b1, j->first.second)) { + pos = std::max (pos, j->second + space + (ref_pos - start)); + } } - limits.add (b1, b2, min_pos + (end_pos - ref_pos), join_op); + if (pitch > 0 && ! db::coord_traits::equal (pos, min_pos)) { + pos = db::coord_traits::rounded (ceil (double (pos) / double (pitch) - 1e-10) * double (pitch)); + } + + if (horizontally) { + box.move (db::vector (pos - ref_pos, 0)); + } else { + box.move (db::vector (0, pos - ref_pos)); + } + + limits.add (b1, b2, pos + (end_pos - ref_pos), join_op); } @@ -266,24 +309,26 @@ public: * @brief Distributes the stored objects in vertical direction only * * @param ref The reference location (-1: bottom, 0: center, 1: top) + * @param refp The alignment in the other (horizontal) direction (-1: left, 0: center, 1: right, other: leave as is) * @param pitch The distribution pitch (grid) or 0 for no pitch * @param space The minimum space between the objects */ - void distribute_v (int ref, coord_type pitch, coord_type space) + void distribute_v (int ref, int refp, coord_type pitch, coord_type space) { - do_distribute_1d (ref, pitch, space); + do_distribute_1d (ref, refp, pitch, space); } /** * @brief Distributes the stored objects in horizontal direction only * * @param ref The reference location (-1: left, 0: center, 1: right) + * @param refp The alignment in the other (vertical) direction (-1: bottom, 0: center, 1: top, other: leave as is) * @param pitch The distribution pitch (grid) or 0 for no pitch * @param space The minimum space between the objects */ - void distribute_h (int ref, coord_type pitch, coord_type space) + void distribute_h (int ref, int refp, coord_type pitch, coord_type space) { - do_distribute_1d (ref, pitch, space); + do_distribute_1d (ref, refp, pitch, space); } /** @@ -305,8 +350,8 @@ public: // The algorithm is this: // 1.) Bin the boxes according to their positions in horizontal and vertical direction. // This forms the potential columns and rows - // 2.) Compute the actual column and row positions by applying space and pitch constraints: - // horizontally first, then vertically (TODO: we could try both ways and test which one is better) + // 2.) Compute the row and column widths and heights as the maximum of their content + // 3.) position the objects inside these cells std::vector > indexed_boxes; indexed_boxes.reserve (m_objects.size ()); @@ -329,10 +374,134 @@ public: std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (vref)); do_bin (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins); - compute_positions (href, hpitch, hspace, hbins, m_objects); - compute_positions (vref, vpitch, vspace, vbins, m_objects); + std::vector > > cells; - // Final adjustments + cells.resize (hbins.size ()); + for (size_t i = 0; i < hbins.size (); ++i) { + cells [i].resize (vbins.size ()); + } + + { + + std::vector hbin_for_index; + hbin_for_index.resize (indexed_boxes.size (), size_t (0)); + for (std::vector >::const_iterator i = hbins.begin (); i != hbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + hbin_for_index [*j] = i - hbins.begin (); + } + } + + for (std::vector >::const_iterator i = vbins.begin (); i != vbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + cells [hbin_for_index [*j]][i - vbins.begin ()].push_back (*j); + } + } + + } + + std::vector cell_widths, cell_heights; + cell_widths.resize (hbins.size (), 0); + cell_heights.resize (vbins.size (), 0); + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0, hcell = 0; + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + // NOTE: intra-cell objects are distributed horizontally + wcell += w; + + coord_type h = m_objects [*k].first.height () + vspace; + if (vpitch > 0) { + h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); + } + hcell = std::max (hcell, h); + + } + + cell_widths [i - cells.begin ()] = std::max (cell_widths [i - cells.begin ()], wcell); + cell_heights [j - i->begin ()] = std::max (cell_heights [j - i->begin ()], hcell); + + } + + } + + std::vector cell_xpos, cell_ypos; + cell_xpos.reserve (cell_widths.size ()); + cell_ypos.reserve (cell_heights.size ()); + + coord_type x = 0, y = 0; + for (typename std::vector::const_iterator i = cell_widths.begin (); i != cell_widths.end (); ++i) { + cell_xpos.push_back (x); + x += *i; + } + for (typename std::vector::const_iterator i = cell_heights.begin (); i != cell_heights.end (); ++i) { + cell_ypos.push_back (y); + y += *i; + } + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0; + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + // NOTE: intra-cell objects are distributed horizontally + wcell += w; + + } + + coord_type x = cell_xpos [i - cells.begin ()]; + if (href == 0) { + x += (cell_widths [i - cells.begin ()] - wcell) / 2; + } else if (href > 0) { + x += (cell_widths [i - cells.begin ()] - wcell); + } + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + + coord_type h = m_objects [*k].first.height () + vspace; + if (vpitch > 0) { + h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); + } + + coord_type y = cell_ypos [j - i->begin ()]; + if (vref == 0) { + y += (cell_heights [j - i->begin ()] - h) / 2; + } else if (href > 0) { + y += (cell_heights [j - i->begin ()] - h); + } + + m_objects [*k].first.move (db::point (x, y) - m_objects [*k].first.p1 ()); + + // NOTE: intra-cell objects are distributed horizontally + x += w; + + } + + } + + } + + // Final adjustments - align the whole matrix with the original bounding box Box new_all; for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { @@ -352,7 +521,7 @@ private: objects m_objects; template - void do_distribute_1d (int ref, coord_type pitch, coord_type space) + void do_distribute_1d (int ref, int refp, coord_type pitch, coord_type space) { if (m_objects.size () < 2) { return; @@ -402,7 +571,24 @@ private: } for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + i->first.move (mv); + + if (refp >= -1 && refp <= 1) { + + coord_type dp = box_position (all, refp) - box_position (i->first, refp); + + db::vector mvp; + if (horizontally) { + mvp = db::vector (0, dp); + } else { + mvp = db::vector (dp, 0); + } + + i->first.move (mvp); + + } + } } }; diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 614e1f4e4..71b210ac7 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -1869,7 +1869,7 @@ MainService::cm_distribute () if (! distribute_options_dialog ()->exec_dialog (view (), m_hdistribute, m_distribute_hmode, m_distribute_hpitch, m_distribute_hspace, m_vdistribute, m_distribute_vmode, m_distribute_vpitch, m_distribute_vspace, - m_align_visible_layers)) { + m_distribute_visible_layers)) { return; } @@ -1925,13 +1925,16 @@ MainService::cm_distribute () } + int href = int (m_distribute_hmode - 2); + int vref = 2 - int (m_distribute_vmode); + if (m_hdistribute && m_vdistribute) { - placer.distribute_matrix (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace, - 2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + placer.distribute_matrix (href, m_distribute_hpitch, m_distribute_hspace, + vref, m_distribute_vpitch, m_distribute_vspace); } else if (m_hdistribute) { - placer.distribute_h (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace); + placer.distribute_h (href, vref, m_distribute_hpitch, m_distribute_hspace); } else if (m_vdistribute) { - placer.distribute_v (2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + placer.distribute_v (vref, href, m_distribute_vpitch, m_distribute_vspace); } transformations.resize (org_boxes.size ()); diff --git a/src/edt/unit_tests/edtDistributeTests.cc b/src/edt/unit_tests/edtDistributeTests.cc index aa5927a81..1a3288f61 100644 --- a/src/edt/unit_tests/edtDistributeTests.cc +++ b/src/edt/unit_tests/edtDistributeTests.cc @@ -53,27 +53,42 @@ TEST(1) edt::distributed_placer p; p = placer; - p.distribute_h (-1, 0, 100); + p.distribute_h (-1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,0;300,200)[0],(400,100;450,250)[3],(550,-50;600,150)[4],(700,0;800,500)[1]"); p = placer; - p.distribute_h (-1, 100, 0); + p.distribute_h (-1, -1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,-100;300,100)[0],(400,-100;450,50)[3],(550,-100;600,100)[4],(700,-100;800,400)[1]"); + + p = placer; + p.distribute_h (-1, 0, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,100;100,300)[2],(200,100;300,300)[0],(400,125;450,275)[3],(550,100;600,300)[4],(700,-50;800,450)[1]"); + + p = placer; + p.distribute_h (-1, 1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,300;100,500)[2],(200,300;300,500)[0],(400,350;450,500)[3],(550,300;600,500)[4],(700,0;800,500)[1]"); + + p = placer; + p.distribute_h (-1, 2, 100, 0); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(300,-50;350,150)[4],(400,0;500,500)[1]"); p = placer; - p.distribute_h (-1, 0, 0); + p.distribute_h (-1, 2, 0, 0); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(250,-50;300,150)[4],(300,0;400,500)[1]"); p = placer; - p.distribute_h (1, 0, 100); + p.distribute_h (1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(1300,-100;1400,100)[2],(1500,100;1550,250)[3],(1650,-50;1700,150)[4],(1800,0;1900,200)[0],(2000,0;2100,500)[1]"); p = placer; - p.distribute_v (-1, 0, 100); + p.distribute_v (-1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(1050,200;1100,400)[4],(1000,500;1100,700)[0],(2000,800;2100,1300)[1],(1000,1400;1050,1550)[3]"); } @@ -96,3 +111,49 @@ TEST(2) EXPECT_EQ (plc2string (p), "(-5,0;95,100)[0],(-5,100;95,200)[1],(95,100;195,200)[2],(95,0;195,100)[3]"); } +TEST(3) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 20, 1, 23), 0); + placer.insert (db::Box (3, 8, 8, 19), 1); + placer.insert (db::Box (6, 0, 12, 5), 2); + placer.insert (db::Box (13, 1, 19, 6), 3); + placer.insert (db::Box (10, 16, 11, 17), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, -1, 0, 0); + + EXPECT_EQ (plc2string (p), "(0,17;1,20)[0],(1,5;6,16)[1],(6,0;12,5)[2],(13,0;19,5)[3],(12,16;13,17)[4]"); +} + +TEST(4) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 16, 1, 20), 0); + placer.insert (db::Box (0, 8, 5, 19), 1); + placer.insert (db::Box (0, 0, 12, 5), 2); + placer.insert (db::Box (12, 1, 19, 6), 3); + placer.insert (db::Box (0, 18, 1, 19), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, 1, 0, 0); + + EXPECT_EQ (plc2string (p), "(6,9;7,13)[0],(1,9;6,20)[1],(0,4;12,9)[2],(12,4;19,9)[3],(0,9;1,10)[4]"); + + p = placer; + p.distribute_matrix (1, 10, 0, -1, 10, 0); + + EXPECT_EQ (plc2string (p), "(-38,30;-37,34)[0],(-18,10;-13,21)[1],(-8,0;4,5)[2],(12,0;19,5)[3],(-28,30;-27,31)[4]"); + + p = placer; + p.distribute_matrix (1, 0, 1, 1, 0, 1); + + EXPECT_EQ (plc2string (p), "(-9,16;-8,20)[0],(-7,9;-2,20)[1],(-1,3;11,8)[2],(12,3;19,8)[3],(-11,19;-10,20)[4]"); +} +