/*! Add a single layout object shape into the quadTree. It first checks whether or not another layout object is already placed into this quadTree. If not it simply links the shape and exits. If another object is already here, then the new overlap area is calculated. \n In case the new area is the same as the existing one, then the object might be (possibly) fited into one of the childrens quadTree. To check this fitintree() method is called. If this is unsuccessfull, just then the layout object is linked to this quadTree. \n If the new overlapping area is bigger that the existing one, then the layout object is linked to the current quadTree after what the current quadTree as well as its successors has to be rebuild using resort().\n The method might be called recursively via fitintree() method. */ void laydata::quadTree::add(tdtdata* shape) { DBbox shovl = shape->overlap();shovl.normalize(); if (empty()) { // first shape in the container _overlap = shovl; _first = shape;shape->nextis(NULL); } else { // save the old overlap DBbox oldovl = _overlap; // calculate the new container overlap _overlap.overlap(shovl); float areaold = oldovl.area(); float areanew = _overlap.area(); // The equation below produce problems with severe consequences. // It seems to be because of the type of the conversion // if (oldovl.area() == _overlap->area()) { if (areaold == areanew) { // if the overlapping box hasn't changed, // try to fit the shape into subtree if ((areanew <= 4 * shovl.area()) || !fitintree(shape)) { // shape doesn't fit into the subtree, so place it here shape->nextis(_first); _first = shape; } } else { // the overlapping box has had blown-up shape->nextis(_first); _first = shape; resort(); // re-sort the entire tree } } }
/*! Build a new quadTree structure for the tdtdata in the inlist. The method is using the existing _overlap variable. For every layout shape fitinsubtree() method is called in a try to put the data in the child quadTree. At the end the method is called for every of the child structures. */ void laydata::quadTree::sort(dataList& inlist) { // if the input list is empty - nothing to do! if (0 == inlist.size()) return; dataList::iterator DI = inlist.begin(); // if the list contains only one component - link it and run away if (1 == inlist.size()) { DI->first->nextis(NULL); _first = DI->first; return; } byte i; // the overlapping box of the currnet shape // DBbox shovl(TP(0,0)); DBbox shovl = DEFAULT_OVL_BOX; // the maximum possible overlapping boxes of the 4 children DBbox maxsubbox[4] = {DEFAULT_OVL_BOX, DEFAULT_OVL_BOX, DEFAULT_OVL_BOX, DEFAULT_OVL_BOX}; for (i = 0; i < 4; i++) maxsubbox[i] = _overlap.getcorner(i); // the sub-lists data will be sorted in dataList sublist[4]; // which is the child where current shape fits int fitinsubbox; // initialize the iterator float sharea, totalarea = _overlap.area(); while (inlist.end() != DI) { // get the overlap of the current shape shovl = DI->first->overlap();shovl.normalize(); sharea = shovl.area(); // Check it fits in some of the children if (totalarea <= 4 * sharea || (-1 == (fitinsubbox = fitsubtree(shovl, maxsubbox)))) { // no fit. The shape is sorted in the current tree DI->first->nextis(_first); _first = DI->first; } else { // fits in sub-tree fitinsubbox sublist[fitinsubbox].push_back(*DI); // check this child already exists if (_quads[fitinsubbox]) // yes ? _quads[fitinsubbox]->_overlap.overlap(shovl); else { // create the child, initialize the overlapping box _quads[fitinsubbox] = DEBUG_NEW quadTree(); _quads[fitinsubbox]->_overlap = shovl; } } // in all cases get rid of the current shape pointer. It is already sorted DI = inlist.erase(DI); } // at this point inlist MUST be empty - split over max 4+this quadTrees // now go and sort the children - if any for(i = 0; i < 4; i++) if (_quads[i]) _quads[i]->sort(sublist[i]); }
/*! A temporary Draw (during move/copy operations) of the container contents on the screen using the virtual quadTree::tmp_draw() method of the parent object. */ void laydata::tdtlayer::tmp_draw(const layprop::DrawProperties& drawprop, ctmqueue& transtack) const { // check the entire layer for clipping... DBbox clip = drawprop.clipRegion(); if (empty()) return; DBbox areal = overlap() * transtack.front(); areal.normalize(); if (clip.cliparea(areal) == 0) return; else { areal = areal * drawprop.ScrCTM(); if (areal.area() < MIN_VISUAL_AREA) return; } quadTree::tmp_draw(drawprop, transtack); }
/*! Temporary draw of the container contents on the screen using the virtual tmp_draw methods of the tdtddata objects. This happens only if the current quadTree object is visible. Current clip region data is obtained from LayoutCanvas. In a sence this method is the same as openGL_draw without fill and not handling selected shapes*/ void laydata::quadTree::tmp_draw(const layprop::DrawProperties& drawprop, ctmqueue& transtack) const { if (empty()) return; // check the entire holder for clipping... DBbox clip = drawprop.clipRegion(); DBbox areal = _overlap * transtack.front(); areal.normalize(); if (clip.cliparea(areal) == 0) return; else { areal = areal * drawprop.ScrCTM(); if (areal.area() < MIN_VISUAL_AREA) return; } tdtdata* wdt = _first; while(wdt) { wdt->tmp_draw(drawprop, transtack); wdt = wdt->next(); } for(byte i = 0; i < 4; i++) if (_quads[i]) _quads[i]->tmp_draw(drawprop, transtack); }
/*! Draw the contents of the container on the screen using the virtual openGL_draw methods of the tdtddata objects. This happens only if the current quadTree object is visible. Current clip region data is obtained from LayoutCanvas. Draws also the select marks in case shape is selected. \n This is the cherry of the quadTree algorithm cake*/ void laydata::quadTree::openGL_draw(ctmstack& transtack, const layprop::DrawProperties& drawprop, const dataList* slst) const { if (empty()) return; // check the entire holder for clipping... DBbox clip = drawprop.clipRegion(); DBbox areal = _overlap * transtack.top(); areal.normalize(); if (clip.cliparea(areal) == 0) return; else { areal = areal * drawprop.ScrCTM(); if (areal.area() < MIN_VISUAL_AREA) return; // std::cout << " ... with area " << areal.area() << "\n"; } tdtdata* wdt = _first; // The drawing will be faster like this for the cells without selected shapes // that will be the wast majority of the cases. A bit bigger code though. // Seems the bargain is worth it. if (slst) while(wdt) { wdt->openGL_draw(transtack,drawprop); // in case the shape is somehow selected... if (sh_selected == wdt->status()) wdt->draw_select(transtack.top()); else if (sh_partsel == wdt->status()) { dataList::const_iterator SI; for (SI = slst->begin(); SI != slst->end(); SI++) if (SI->first == wdt) break; assert(SI != slst->end()); wdt->draw_select(transtack.top(), SI->second); } wdt = wdt->next(); } else // if there are no selected shapes while(wdt) { wdt->openGL_draw(transtack,drawprop); wdt = wdt->next(); } for(byte i = 0; i < 4; i++) if (_quads[i]) _quads[i]->openGL_draw(transtack, drawprop, slst); }
/*! Draw the contents of the container on the screen using the virtual openGL_draw methods of the tdtddata objects. This happens only if the current quadTree object is visible. Current clip region data is obtained from LayoutCanvas. Draws also the select marks in case shape is selected. \n This is the cherry of the quadTree algorithm cake*/ void laydata::quadTree::openGL_draw(layprop::DrawProperties& drawprop, const dataList* slst, bool fill, bool bound) const { if (empty()) return; // check the entire holder for clipping... DBbox clip = drawprop.clipRegion(); DBbox areal = _overlap * drawprop.topCTM(); areal.normalize(); if (clip.cliparea(areal) == 0) return; else { areal = areal * drawprop.ScrCTM(); if (areal.area() < MIN_VISUAL_AREA) return; // std::cout << " ... with area " << areal.area() << "\n"; } tdtdata* wdt = _first; // bool fill = drawprop.getCurrentFill(); // The drawing will be faster like this for the cells without selected shapes // that will be the wast majority of the cases. A bit bigger code though. // Seems the bargain is worth it. if (slst) { while(wdt) { pointlist points; // precalculate drawing data wdt->openGL_precalc(drawprop, points); if (0 != points.size()) { // draw the shape fill (contents of refs, arefs and texts) if (fill) wdt->openGL_drawfill(drawprop, points); // draw the outline of the shapes and overlapping boxes if (bound) wdt->openGL_drawline(drawprop, points); if ((sh_selected == wdt->status()) || (sh_partsel == wdt->status())) { drawprop.setLineProps(true); if (sh_selected == wdt->status()) wdt->openGL_drawsel(points, NULL); else if (sh_partsel == wdt->status()) { dataList::const_iterator SI; for (SI = slst->begin(); SI != slst->end(); SI++) if (SI->first == wdt) break; assert(SI != slst->end()); wdt->openGL_drawsel(points, SI->second); } drawprop.setLineProps(false); } wdt->openGL_postclean(drawprop, points); } wdt = wdt->next(); } } else { // if there are no selected shapes while(wdt) { pointlist points; // precalculate drawing data wdt->openGL_precalc(drawprop, points); // draw the shape fill (contents of refs, arefs and texts) if (fill) wdt->openGL_drawfill(drawprop, points); // draw the outline of the shapes and overlapping boxes if (bound) wdt->openGL_drawline(drawprop, points); // clean-up wdt->openGL_postclean(drawprop, points); wdt = wdt->next(); } } /* // The drawing will be faster like this for the cells without selected shapes // that will be the wast majority of the cases. A bit bigger code though. // Seems the bargain is worth it. if (slst) while(wdt) { wdt->openGL_draw(drawprop); // in case the shape is somehow selected... if (sh_selected == wdt->status()) wdt->draw_select(drawprop.topCTM()); else if (sh_partsel == wdt->status()) { dataList::const_iterator SI; for (SI = slst->begin(); SI != slst->end(); SI++) if (SI->first == wdt) break; assert(SI != slst->end()); wdt->draw_select(drawprop.topCTM(), SI->second); } wdt = wdt->next(); } else // if there are no selected shapes while(wdt) { wdt->openGL_draw(drawprop); wdt = wdt->next(); }*/ for(byte i = 0; i < 4; i++) if (_quads[i]) _quads[i]->openGL_draw(drawprop, slst, fill, bound); }