///////////////////////////////////////////////////////////////////////////// // Name: block.cpp // Purpose: Rectangular selection storage classes for ints and doubles // Author: John Labenski // Created: 07/01/02 // Copyright: (c) John Labenski 2004 // Licence: wxWidgets ///////////////////////////////////////////////////////////////////////////// #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "block.h" #endif // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP //#include "wx/object.h" #endif // WX_PRECOMP #include "wx/things/block.h" // use this to check to see if there is any overlap after minimizing //#define CHECK_BLOCK_OVERLAP 1 #define PRINT_BLOCK(msg, b) { wxPrintf(wxT("Block '%s' %lg %lg %lg %lg\n"), msg, (double)(b).m_x1, (double)(b).m_y1, (double)(b).m_x2, (double)(b).m_y2); } wxBlockInt const wxEmptyBlockInt(0, 0, -1, -1); wxBlockDouble const wxEmptyBlockDouble(0, 0, -1, -1); #include "wx/arrimpl.cpp" WX_DEFINE_OBJARRAY(wxArrayBlockInt); WX_DEFINE_OBJARRAY(wxArrayBlockDouble); WX_DEFINE_OBJARRAY(wxArrayBlockIntSelection); WX_DEFINE_OBJARRAY(wxArrayBlockDoubleSelection); // ---------------------------------------------------------------------------- // Sorting functions for wxBlockInt // ---------------------------------------------------------------------------- static int wxCMPFUNC_CONV wxblockint_sort_topleft_bottomright( wxBlockInt **a, wxBlockInt **b) { register int y = ((*a)->m_y1 - (*b)->m_y1); if (y < 0) return -1; if (y == 0) return ((*a)->m_x1 - (*b)->m_x1); return 1; } static int wxCMPFUNC_CONV wxblockint_sort_topright_bottomleft( wxBlockInt **a, wxBlockInt **b) { register int y = ((*a)->m_y1 - (*b)->m_y1); if (y < 0) return -1; if (y == 0) return ((*a)->m_x2 - (*b)->m_x2); return 1; } static int wxCMPFUNC_CONV wxblockint_sort_bottomleft_topright( wxBlockInt **a, wxBlockInt **b) { register int y = ((*a)->m_y2 - (*b)->m_y2); if (y > 0) return -1; if (y == 0) return ((*a)->m_x1 - (*b)->m_x1); return 1; } static int wxCMPFUNC_CONV wxblockint_sort_bottomright_topleft( wxBlockInt **a, wxBlockInt **b) { register int y = ((*a)->m_y2 - (*b)->m_y2); if (y > 0) return -1; if (y == 0) return ((*a)->m_x2 - (*b)->m_x2); return 1; } static int wxCMPFUNC_CONV wxblockint_sort_largest_to_smallest( wxBlockInt **a, wxBlockInt **b) { return (*a)->IsLarger(**b); } static int wxCMPFUNC_CONV wxblockint_sort_smallest_to_largest( wxBlockInt **a, wxBlockInt **b) { return -(*a)->IsLarger(**b); } void wxArrayBlockIntSort(wxArrayBlockInt &blocks, wxBlockSort_Type type) { switch (type) { case wxBLOCKSORT_TOPLEFT_BOTTOMRIGHT : blocks.Sort(wxblockint_sort_topleft_bottomright); break; case wxBLOCKSORT_TOPRIGHT_BOTTOMLEFT : blocks.Sort(wxblockint_sort_topright_bottomleft); break; case wxBLOCKSORT_BOTTOMLEFT_TOPRIGHT : blocks.Sort(wxblockint_sort_bottomleft_topright); break; case wxBLOCKSORT_BOTTOMRIGHT_TOPLEFT : blocks.Sort(wxblockint_sort_bottomright_topleft); break; case wxBLOCKSORT_SMALLEST_TO_LARGEST : blocks.Sort(wxblockint_sort_smallest_to_largest); break; case wxBLOCKSORT_LARGEST_TO_SMALLEST : blocks.Sort(wxblockint_sort_largest_to_smallest); break; default : wxFAIL_MSG(wxT("unknown block sort type")); } } // ---------------------------------------------------------------------------- // Sorting functions for wxBlockDouble // ---------------------------------------------------------------------------- static int wxCMPFUNC_CONV wxblockdouble_sort_topleft_bottomright( wxBlockDouble **a, wxBlockDouble **b) { register wxDouble y = ((*a)->m_y1 - (*b)->m_y1); if (y < 0) return -1; if (y == 0) return int((*a)->m_x1 - (*b)->m_x1); return 1; } static int wxCMPFUNC_CONV wxblockdouble_sort_topright_bottomleft( wxBlockDouble **a, wxBlockDouble **b) { register wxDouble y = ((*a)->m_y1 - (*b)->m_y1); if (y < 0) return -1; if (y == 0) return int((*a)->m_x2 - (*b)->m_x2); return 1; } static int wxCMPFUNC_CONV wxblockdouble_sort_bottomleft_topright( wxBlockDouble **a, wxBlockDouble **b) { register wxDouble y = ((*a)->m_y2 - (*b)->m_y2); if (y > 0) return -1; if (y == 0) return int((*a)->m_x1 - (*b)->m_x1); return 1; } static int wxCMPFUNC_CONV wxblockdouble_sort_bottomright_topleft( wxBlockDouble **a, wxBlockDouble **b) { register wxDouble y = ((*a)->m_y2 - (*b)->m_y2); if (y > 0) return -1; if (y == 0) return int((*a)->m_x2 - (*b)->m_x2); return 1; } static int wxCMPFUNC_CONV wxblockdouble_sort_largest_to_smallest( wxBlockDouble **a, wxBlockDouble **b) { return (*a)->IsLarger(**b); } static int wxCMPFUNC_CONV wxblockdouble_sort_smallest_to_largest( wxBlockDouble **a, wxBlockDouble **b) { return -(*a)->IsLarger(**b); } void wxArrayBlockDoubleSort(wxArrayBlockDouble &blocks, wxBlockSort_Type type) { switch (type) { case wxBLOCKSORT_TOPLEFT_BOTTOMRIGHT : blocks.Sort(wxblockdouble_sort_topleft_bottomright); break; case wxBLOCKSORT_TOPRIGHT_BOTTOMLEFT : blocks.Sort(wxblockdouble_sort_topright_bottomleft); break; case wxBLOCKSORT_BOTTOMLEFT_TOPRIGHT : blocks.Sort(wxblockdouble_sort_bottomleft_topright); break; case wxBLOCKSORT_BOTTOMRIGHT_TOPLEFT : blocks.Sort(wxblockdouble_sort_bottomright_topleft); break; case wxBLOCKSORT_SMALLEST_TO_LARGEST : blocks.Sort(wxblockdouble_sort_smallest_to_largest); break; case wxBLOCKSORT_LARGEST_TO_SMALLEST : blocks.Sort(wxblockdouble_sort_largest_to_smallest); break; default : wxFAIL_MSG(wxT("unknown block sort type")); } } //============================================================================= // wxBlockInt //============================================================================= #define TEST_BLOCKS #ifdef TEST_BLOCKS void TestBlocks() { printf("Start Testing blocks -----------------------------------------\n"); wxBlockInt b1(1,1,4,4); wxBlockInt b2(5,4,10,11); PRINT_BLOCK("b1", b1) PRINT_BLOCK("b2", b2) wxBlockInt iB; iB.Intersect(b1, b2, &iB); PRINT_BLOCK("Intersect b1 b2", iB) wxBlockInt uB; uB.Union(b1, b2, &uB); PRINT_BLOCK("Union b1 b2", uB) printf("Touches b1 b2 %d %d\n", b1.Touches(b2), b2.Touches(b1)); b1 = wxBlockInt(2,3,7,9); b2 = wxBlockInt(8,3,8,3); printf("Touches b1 b2 %d %d\n", b1.Touches(b2), b2.Touches(b1)); b1 = wxBlockInt(2,3,7,9); b2 = wxBlockInt(1,3,1,3); printf("Touches b1 b2 %d %d\n", b1.Touches(b2), b2.Touches(b1)); iB.Intersect(b1, b2, &iB); PRINT_BLOCK("Intersect b1 b2", iB) b1 = wxBlockInt(2,3,7,9); b2 = wxBlockInt(2,2,2,2); printf("Touches b1 b2 %d %d\n", b1.Touches(b2), b2.Touches(b1)); b1 = wxBlockInt(2,3,7,9); b2 = wxBlockInt(7,10,7,10); printf("Touches b1 b2 %d %d\n", b1.Touches(b2), b2.Touches(b1)); printf("End Testing blocks -----------------------------------------\n"); fflush(stdout); } #endif //TEST_BLOCKS bool wxBlockInt::Touches(const wxBlockInt &b) const // see Intersects { //if (((wxMax(m_x1, b.m_x1)) <= (wxMin(m_x2, b.m_x2))) && // ((wxMax(m_y1, b.m_y1)) <= (wxMin(m_y2, b.m_y2)))) // return true; return Intersects(wxBlockInt(b.m_x1-1, b.m_y1-1, b.m_x2+1, b.m_y2+1)); /* wxInt32 left = wxMax( m_x1, b.m_x1 ); wxInt32 right = wxMin( m_x2, b.m_x2 ); if (labs(left - right) <= 1) { wxInt32 top = wxMax( m_y1, b.m_y1 ); wxInt32 bottom = wxMin( m_y2, b.m_y2 ); if (labs(top - bottom) <= 1) return true; } return false; */ } bool wxBlockInt::Combine(const wxBlockInt &b) { if (!Touches(b)) return false; if (Contains(b)) return true; if (b.Contains(*this)) { *this = b; return true; } wxBlockInt unionBlock; Union( *this, b, &unionBlock ); if (unionBlock.IsEmpty()) return false; // at least one of the two blocks has to be at each corner of the union if (((unionBlock.GetLeftTop() == GetLeftTop()) || (unionBlock.GetLeftTop() == b.GetLeftTop())) && ((unionBlock.GetRightTop() == GetRightTop()) || (unionBlock.GetRightTop() == b.GetRightTop())) && ((unionBlock.GetLeftBottom() == GetLeftBottom()) || (unionBlock.GetLeftBottom() == b.GetLeftBottom())) && ((unionBlock.GetRightBottom() == GetRightBottom()) || (unionBlock.GetRightBottom() == b.GetRightBottom())) ) { *this = unionBlock; return true; } return false; } bool wxBlockInt::Combine( const wxBlockInt &block, wxBlockInt &top, wxBlockInt &bottom, wxBlockInt &left, wxBlockInt &right) const { top = bottom = left = right = wxEmptyBlockInt; wxBlockInt iBlock; Intersect(*this, block, &iBlock); if (iBlock.IsEmpty()) return false; // nothing to combine if (iBlock == *this) return true; // can combine all of this, no leftover bool combined = false; if ( block.m_y1 < m_y1 ) { top = wxBlockInt( block.m_x1, block.m_y1, block.m_x2, m_y1-1 ); combined = true; } if ( block.m_y2 > m_y2 ) { bottom = wxBlockInt( block.m_x1, m_y2+1, block.m_x2, block.m_y2 ); combined = true; } if ( block.m_x1 < m_x1 ) { left = wxBlockInt( block.m_x1, iBlock.m_y1, m_x1-1, iBlock.m_y2 ); combined = true; } if ( block.m_x2 > m_x2 ) { right = wxBlockInt( m_x2+1, iBlock.m_y1, block.m_x2, iBlock.m_y2 ); combined = true; } return combined; } bool wxBlockInt::Delete( const wxBlockInt &block, wxBlockInt &top, wxBlockInt &bottom, wxBlockInt &left, wxBlockInt &right) const { top = bottom = left = right = wxEmptyBlockInt; wxBlockInt iBlock; Intersect(*this, block, &iBlock); if (iBlock.IsEmpty()) return false; // nothing to delete if (iBlock == *this) return true; // can delete all of this, no leftover bool deleted = false; if ( m_y1 < iBlock.m_y1 ) { top = wxBlockInt( m_x1, m_y1, m_x2, iBlock.m_y1-1 ); deleted = true; } if ( GetBottom() > iBlock.GetBottom() ) { bottom = wxBlockInt( m_x1, iBlock.m_y2+1, m_x2, m_y2 ); deleted = true; } if ( m_x1 < iBlock.m_x1 ) { left = wxBlockInt( m_x1, iBlock.m_y1, iBlock.m_x1-1, iBlock.m_y2 ); deleted = true; } if ( GetRight() > iBlock.GetRight() ) { right = wxBlockInt( iBlock.m_x2+1, iBlock.m_y1, m_x2, iBlock.m_y2 ); deleted = true; } return deleted; } //============================================================================= // wxBlockDouble //============================================================================= bool wxBlockDouble::Touches(const wxBlockDouble &b) const // see Intersects { if (((wxMax(m_x1, b.m_x1)) <= (wxMin(m_x2, b.m_x2))) && ((wxMax(m_y1, b.m_y1)) <= (wxMin(m_y2, b.m_y2)))) return true; return false; } bool wxBlockDouble::Combine(const wxBlockDouble &b) { if (!Touches(b)) return false; if (Contains(b)) return true; if (b.Contains(*this)) { *this = b; return true; } wxBlockDouble unionBlock; Union( *this, b, &unionBlock ); if (unionBlock.IsEmpty()) return false; // at least one of the two blocks has to be at each corner of the union if (((unionBlock.GetLeftTop() == GetLeftTop()) || (unionBlock.GetLeftTop() == b.GetLeftTop())) && ((unionBlock.GetRightTop() == GetRightTop()) || (unionBlock.GetRightTop() == b.GetRightTop())) && ((unionBlock.GetLeftBottom() == GetLeftBottom()) || (unionBlock.GetLeftBottom() == b.GetLeftBottom())) && ((unionBlock.GetRightBottom() == GetRightBottom()) || (unionBlock.GetRightBottom() == b.GetRightBottom())) ) { *this = unionBlock; return true; } return false; } bool wxBlockDouble::Combine( const wxBlockDouble &block, wxBlockDouble &top, wxBlockDouble &bottom, wxBlockDouble &left, wxBlockDouble &right) const { top = bottom = left = right = wxEmptyBlockDouble; wxBlockDouble iBlock; Intersect(*this, block, &iBlock); if (iBlock.IsEmpty()) return false; // nothing to combine if (iBlock == *this) return true; // can combine all of this, no leftover bool combined = false; if ( block.m_y1 < m_y1 ) { top = wxBlockDouble( block.m_x1, block.m_y1, block.m_x2, m_y1 ); combined = true; } if ( block.m_y2 > m_y2 ) { bottom = wxBlockDouble( block.m_x1, m_y2, block.m_x2, block.m_y2 ); combined = true; } if ( block.m_x1 < m_x1 ) { left = wxBlockDouble( block.m_x1, iBlock.m_y1, m_x1, iBlock.m_y2 ); combined = true; } if ( block.m_x2 > m_x2 ) { right = wxBlockDouble( m_x2, iBlock.m_y1, block.m_x2, iBlock.m_y2 ); combined = true; } return combined; } bool wxBlockDouble::Delete( const wxBlockDouble &block, wxBlockDouble &top, wxBlockDouble &bottom, wxBlockDouble &left, wxBlockDouble &right) const { top = bottom = left = right = wxEmptyBlockDouble; wxBlockDouble iBlock; Intersect(*this, block, &iBlock); if (iBlock.IsEmpty()) return false; // nothing to delete if (iBlock == *this) return true; // can delete all of this, no leftover bool deleted = false; if ( m_y1 < iBlock.m_y1 ) { top = wxBlockDouble( m_x1, m_y1, m_x2, iBlock.m_y1 ); deleted = true; } if ( m_y2 > iBlock.m_y2 ) { bottom = wxBlockDouble( m_x1, iBlock.m_y2, m_x2, m_y2 ); deleted = true; } if ( m_x1 < iBlock.m_x1 ) { left = wxBlockDouble( m_x1, iBlock.m_y1, iBlock.m_x1, iBlock.m_y2 ); deleted = true; } if ( m_x2 > iBlock.m_x2 ) { right = wxBlockDouble( iBlock.m_x2, iBlock.m_y1, m_x2, iBlock.m_y2 ); deleted = true; } return deleted; } //============================================================================= // wxBlockIntSelection //============================================================================= wxBlockInt wxBlockIntSelection::GetBlock( int index ) const { wxCHECK_MSG((index>=0) && (index= m_blocks[n].m_x1) && (col <= m_blocks[n].m_x2)) { wxRangeInt range(m_blocks[n].m_y1, m_blocks[n].m_y2); ranges.Add(range); } } return ranges; } wxArrayRangeInt wxBlockIntSelection::GetBlockRow(int row) const { wxArrayRangeInt ranges; register int n, count = m_blocks.GetCount(); for (n=0; n= m_blocks[n].m_y1) && (row <= m_blocks[n].m_y2)) ranges.Add(wxRangeInt(m_blocks[n].m_x1, m_blocks[n].m_x2)); } return ranges; } #endif // USE_wxRANGE wxBlockInt wxBlockIntSelection::GetBoundingBlock() const { register int n, count = m_blocks.GetCount(); if (count == 0) return wxEmptyBlockInt; wxBlockInt bound = m_blocks[0]; for (n=1; n 0) ? n - 1 : -1; if (!top.IsEmpty()) m_blocks.Add(top); if (!bottom.IsEmpty()) m_blocks.Add(bottom); if (!left.IsEmpty()) m_blocks.Add(left); if (!right.IsEmpty()) m_blocks.Add(right); } } if (combineNow) Minimize(); return done; } bool wxBlockIntSelection::SelectBlock( const wxBlockInt &block, bool combineNow, wxArrayBlockInt *addedBlocks ) { wxCHECK_MSG(!block.IsEmpty(), false, wxT("Invalid block") ); //TestBlocks(); wxArrayBlockInt extraBlocks; wxArrayBlockInt *extra = &extraBlocks; if (addedBlocks != NULL) { addedBlocks->Clear(); extra = addedBlocks; } extra->Add(block); int n, count = m_blocks.GetCount(); wxBlockInt top, bottom, left, right; for (n=0; nGetCount()); k++) { if (m_blocks[n].Combine(extra->Item(k), top, bottom, left, right)) { extra->RemoveAt(k); if (!top.IsEmpty()) extra->Add(top); if (!bottom.IsEmpty()) extra->Add(bottom); if (!left.IsEmpty()) extra->Add(left); if (!right.IsEmpty()) extra->Add(right); //DoMinimize( *extra ); n = -1; break; } } } if (extra->GetCount() > 0u) { WX_APPEND_ARRAY(m_blocks, *extra); if (combineNow) Minimize(); return true; } return false; } bool wxBlockIntSelection::Minimize() { bool ret = DoMinimize(m_blocks); Sort(m_sort); return ret; } bool wxBlockIntSelection::DoMinimize(wxArrayBlockInt &blocks) { int n; for (n=0; n<1000; n++) // should probably just take a few { if (!DoDoMinimize(blocks)) break; } #ifdef CHECK_BLOCK_OVERLAP for (size_t a=0; a=0) && (index= m_blocks[n].m_x1) && (col <= m_blocks[n].m_x2)) { wxRangeDouble range(m_blocks[n].m_y1, m_blocks[n].m_y2); ranges.Add(range); } } return ranges; } wxArrayRangeDouble wxBlockDoubleSelection::GetBlockRow(wxDouble row) const { wxArrayRangeDouble ranges; register int n, count = m_blocks.GetCount(); for (n=0; n= m_blocks[n].m_y1) && (row <= m_blocks[n].m_y2)) ranges.Add(wxRangeDouble(m_blocks[n].m_x1, m_blocks[n].m_x2)); } return ranges; } #endif // USE_wxRANGE wxBlockDouble wxBlockDoubleSelection::GetBoundingBlock() const { register int n, count = m_blocks.GetCount(); if (count == 0) return wxEmptyBlockDouble; wxBlockDouble bound = m_blocks[0]; for (n=1; n= m_blocks[n].m_x1) && (y >= m_blocks[n].m_y1) && (x <= m_blocks[n].m_x2) && (y <= m_blocks[n].m_y2) ) return true; } return wxNOT_FOUND; } int wxBlockDoubleSelection::Index( const wxBlockDouble &b ) const { register int n, count = m_blocks.GetCount(); for (n=0; n 0) ? n - 1 : -1; if (!top.IsEmpty()) m_blocks.Add(top); if (!bottom.IsEmpty()) m_blocks.Add(bottom); if (!left.IsEmpty()) m_blocks.Add(left); if (!right.IsEmpty()) m_blocks.Add(right); } } if (combineNow) Minimize(); return done; } bool wxBlockDoubleSelection::SelectBlock( const wxBlockDouble &block, bool combineNow) { // It's valid to select a block with a width and height 0 since that means that point //wxCHECK_MSG(!block.IsEmpty(), false, wxT("Invalid block") ); wxArrayBlockDouble extra; extra.Add(block); wxBlockDouble top, bottom, left, right; for (int n=0; n 0) { done = m_blocks[n].Combine(extra[k], top, bottom, left, right); if (done) { extra.RemoveAt(k); k--; } } else { done = extra[k].Combine(m_blocks[n], top, bottom, left, right); if (done) { m_blocks.RemoveAt(n); n = -1; } } } if (done) { if (!top.IsEmpty()) extra.Add(top); if (!bottom.IsEmpty()) extra.Add(bottom); if (!left.IsEmpty()) extra.Add(left); if (!right.IsEmpty()) extra.Add(right); //DoMinimize( extra ); if (n == -1) break; } } } if (extra.GetCount() > 0u) { WX_APPEND_ARRAY(m_blocks, extra); if (combineNow) Minimize(); return true; } return false; } bool wxBlockDoubleSelection::Minimize() { bool ret = DoMinimize(m_blocks); Sort(m_sort); return ret; } bool wxBlockDoubleSelection::DoMinimize(wxArrayBlockDouble &blocks) { int n; for (n=0; n<1000; n++) // should probably just take < 10 at most { if (!DoDoMinimize(blocks)) break; } #ifdef CHECK_BLOCK_OVERLAP for (size_t a=0; a= int(m_blocks.GetCount()))) return false; // first time here if (m_block_index < 0) { m_block_index = 0; pt = m_pt = m_blocks[m_block_index].GetLeftTop(); return true; } // at end of block swap to new one if (m_pt == m_blocks[m_block_index].GetRightBottom()) { ++m_block_index; if (int(m_blocks.GetCount()) > m_block_index) { pt = m_pt = m_blocks[m_block_index].GetLeftTop(); return true; } else // past end nothing more to check return false; } // at end of col, down to next row if (m_pt.m_x == m_blocks[m_block_index].GetRight()) { m_pt.m_x = m_blocks[m_block_index].m_x1; m_pt.m_y++; pt = m_pt; return true; } // increment the col m_pt.m_x++; pt = m_pt; return true; } bool wxBlockIntSelectionIterator::IsInSelection(const wxPoint2DInt &pt) const { register int n, count = m_blocks.GetCount(); for (n=0; n