#include "tcurves.h" //#include "tpalette.h" #include "tvectorimage.h" #include "tvectorimageP.h" #include "tstroke.h" //#include "tgl.h" #include "tvectorrenderdata.h" #include "tmathutil.h" //#include "tdebugmessage.h" #include "tofflinegl.h" //#include "tcolorstyles.h" #include "tpaletteutil.h" #include "tthreadmessage.h" #include "tsimplecolorstyles.h" #include "tcomputeregions.h" #include //============================================================================= typedef TVectorImage::IntersectionBranch IntersectionBranch; namespace { typedef std::set DisabledStrokeStyles; // uso getDisabledStrokeStyleSet() invece che accedere direttamente alla // variabile per assicurarmi che il tutto funzioni anche quando viene // usato PRIMA del main (per iniziativa di un costruttore di una variabile // globale, p.es.). // per l'idioma: cfr. Modern C++ design, Andrei Alexandrescu, Addison Wesley // 2001, p.133 inline DisabledStrokeStyles &getDisabledStrokeStyleSet() { static DisabledStrokeStyles disabledStokeStyles; return disabledStokeStyles; } inline bool isStrokeStyleEnabled__(int index) { DisabledStrokeStyles &disabledSet = getDisabledStrokeStyleSet(); return disabledSet.find(index) == disabledSet.end(); } } // namespace //============================================================================= /*! Permette di copiare effettuare delle copie delle curve */ /* template class StrokeArrayInsertIterator { Container& container; public: explicit StrokeArrayInsertIterator(Container& Line) :container(Line) {}; StrokeArrayInsertIterator& operator=(const VIStroke* value ) { TStroke *stroke = new TStroke(*(value->m_s)); stroke->setId(value->m_s->getId()); container.push_back(new VIStroke(stroke)); return *this; }; StrokeArrayInsertIterator& operator*() { return *this; } StrokeArrayInsertIterator& operator++() { return *this; } StrokeArrayInsertIterator operator++(int val){ return *this; } }; */ //============================================================================= /*! TVectorImage::Imp: implementation of TVectorImage class \relates TVectorImage */ //============================================================================= TVectorImage::Imp::Imp(TVectorImage *vi) : m_areValidRegions(false) , m_notIntersectingStrokes(false) , m_computeRegions(true) , m_autocloseTolerance(c_newAutocloseTolerance) , m_maxGroupId(1) , m_maxGhostGroupId(1) , m_mutex(new TThread::Mutex()) , m_vi(vi) , m_intersectionData(0) , m_computedAlmostOnce(false) , m_justLoaded(false) , m_insideGroup(TGroupId()) , m_minimizeEdges(true) #ifdef NEW_REGION_FILL , m_regionFinder(0) #endif { #ifdef NEW_REGION_FILL resetRegionFinder(); #endif initRegionsData(); } TVectorImage::Imp::~Imp() { // delete m_regionFinder; deleteRegionsData(); delete m_mutex; } //============================================================================= TVectorImage::TVectorImage(bool loaded) : m_imp(new TVectorImage::Imp(this)) { if (loaded) m_imp->m_justLoaded = true; } //----------------------------------------------------------------------------- TVectorImage::~TVectorImage() {} //----------------------------------------------------------------------------- int TVectorImage::isInsideGroup() const { return m_imp->m_insideGroup.getDepth(); } //----------------------------------------------------------------------------- int TVectorImage::addStrokeToGroup(TStroke *stroke, int strokeIndex) { if (!m_imp->m_strokes[strokeIndex]->m_groupId.isGrouped()) return addStroke(stroke, true); for (int i = m_imp->m_strokes.size() - 1; i >= 0; i--) if (m_imp->m_strokes[i]->m_groupId == m_imp->m_strokes[strokeIndex]->m_groupId) { m_imp->insertStrokeAt( new VIStroke(stroke, m_imp->m_strokes[i]->m_groupId), i + 1); return i + 1; } assert(false); return -1; } //----------------------------------------------------------------------------- int TVectorImage::addStroke(TStroke *stroke, bool discardPoints) { if (discardPoints) { TRectD bBox = stroke->getBBox(); if (bBox.x0 == bBox.x1 && bBox.y0 == bBox.y1) // empty stroke: discard return -1; } if (m_imp->m_insideGroup != TGroupId()) { int i; for (i = m_imp->m_strokes.size() - 1; i >= 0; i--) if (m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[i]->m_groupId)) { m_imp->insertStrokeAt( new VIStroke(stroke, m_imp->m_strokes[i]->m_groupId), i + 1); return i + 1; } } TGroupId gid; if (m_imp->m_strokes.empty() || m_imp->m_strokes.back()->m_groupId.isGrouped() != 0) gid = TGroupId(this, true); else gid = m_imp->m_strokes.back()->m_groupId; m_imp->m_strokes.push_back(new VIStroke(stroke, gid)); m_imp->m_areValidRegions = false; return m_imp->m_strokes.size() - 1; } //----------------------------------------------------------------------------- void TVectorImage::moveStrokes(int fromIndex, int count, int moveBefore) { #ifdef _DEBUG m_imp->checkGroups(); #endif m_imp->moveStrokes(fromIndex, count, moveBefore, true); #ifdef _DEBUG m_imp->checkGroups(); #endif } //----------------------------------------------------------------------------- void TVectorImage::Imp::moveStrokes(int fromIndex, int count, int moveBefore, bool regroup) { assert(fromIndex >= 0 && fromIndex < (int)m_strokes.size()); assert(moveBefore >= 0 && moveBefore <= (int)m_strokes.size()); assert(count > 0); assert(fromIndex != moveBefore); for (int i = 0; i < count; i++) if (fromIndex < moveBefore) moveStroke(fromIndex, moveBefore); else moveStroke(fromIndex + i, moveBefore + i); std::vector changedStrokes; if (regroup) regroupGhosts(changedStrokes); if (!changedStrokes.empty()) notifyChangedStrokes(changedStrokes, std::vector(), false); } //----------------------------------------------------------------------------- void TVectorImage::insertStrokeAt(VIStroke *vs, int strokeIndex, bool recomputeRegions) { m_imp->insertStrokeAt(vs, strokeIndex, recomputeRegions); } /* void TVectorImage::insertStrokeAt(TStroke *stroke, int strokeIndex, const TGroupId& id) { VIStroke* vs; vs = new VIStroke(stroke, id); m_imp->insertStrokeAt(vs, strokeIndex); } */ //----------------------------------------------------------------------------- /* TRectD TVectorImage::addStroke(const std::vector &points) { // era: TStroke *stroke = makeTStroke(points); TStroke *stroke = TStroke::interpolate(points, 5.0); m_imp->m_strokes.push_back(new VIStroke( stroke) ); m_imp->m_areValidRegions = false; return stroke->getBBox(); } */ //----------------------------------------------------------------------------- static bool isRegionWithStroke(TRegion *region, TStroke *s) { for (UINT i = 0; i < region->getEdgeCount(); i++) if (region->getEdge(i)->m_s == s) return true; return false; } //----------------------------------------------------------------------------- static void deleteSubRegionWithStroke(TRegion *region, TStroke *s) { for (int i = 0; i < (int)region->getSubregionCount(); i++) { deleteSubRegionWithStroke(region->getSubregion(i), s); if (isRegionWithStroke(region->getSubregion(i), s)) { TRegion *r = region->getSubregion(i); r->moveSubregionsTo(region); assert(r->getSubregionCount() == 0); region->deleteSubregion(i); delete r; i--; } } } //----------------------------------------------------------------------------- TStroke *TVectorImage::removeStroke(int index, bool doComputeRegions) { return m_imp->removeStroke(index, doComputeRegions); } TStroke *TVectorImage::Imp::removeStroke(int index, bool doComputeRegions) { assert(index >= 0 && index < (int)m_strokes.size()); QMutexLocker sl(m_mutex); VIStroke *stroke = m_strokes[index]; eraseIntersection(index); m_strokes.erase(m_strokes.begin() + index); if (m_computedAlmostOnce) { reindexEdges(index); if (doComputeRegions) computeRegions(); } return stroke->m_s; } //----------------------------------------------------------------------------- void TVectorImage::removeStrokes(const std::vector &toBeRemoved, bool deleteThem, bool recomputeRegions) { m_imp->removeStrokes(toBeRemoved, deleteThem, recomputeRegions); } //----------------------------------------------------------------------------- void TVectorImage::Imp::removeStrokes(const std::vector &toBeRemoved, bool deleteThem, bool recomputeRegions) { QMutexLocker sl(m_mutex); for (int i = toBeRemoved.size() - 1; i >= 0; i--) { assert(i == 0 || toBeRemoved[i - 1] < toBeRemoved[i]); UINT index = toBeRemoved[i]; eraseIntersection(index); if (deleteThem) delete m_strokes[index]; m_strokes.erase(m_strokes.begin() + index); } if (m_computedAlmostOnce && !toBeRemoved.empty()) { reindexEdges(toBeRemoved, false); if (recomputeRegions) computeRegions(); else m_areValidRegions = false; } } //----------------------------------------------------------------------------- void TVectorImage::deleteStroke(int index) { TStroke *stroke = removeStroke(index); delete stroke; } //----------------------------------------------------------------------------- void TVectorImage::deleteStroke(VIStroke *stroke) { UINT index = 0; for (; index < m_imp->m_strokes.size(); index++) if (m_imp->m_strokes[index] == stroke) { deleteStroke(index); return; } } //----------------------------------------------------------------------------- /* void TVectorImage::validateRegionEdges(TStroke* stroke, bool invalidate) { if (invalidate) for (UINT i=0; igetBBox().contains(stroke->getBBox())) for (UINT j=0; jgetEdgeCount(); j++) { TEdge* edge = r->getEdge(j); if (edge->m_s == stroke) edge->m_w0 = edge->m_w1 = -1; } } else for (UINT i=0; igetBBox().contains(stroke->getBBox())) for (UINT j=0; jgetEdgeCount(); j++) { TEdge* edge = r->getEdge(j); if (edge->m_w0==-1) { int index; double t, dummy; edge->m_s->getNearestChunk(edge->m_p0, t, index, dummy); edge->m_w0 = getWfromChunkAndT(edge->m_s, index, t); edge->m_s->getNearestChunk(edge->m_p1, t, index, dummy); edge->m_w1 = getWfromChunkAndT(edge->m_s, index, t); } } } } */ //----------------------------------------------------------------------------- UINT TVectorImage::getStrokeCount() const { return m_imp->m_strokes.size(); } //----------------------------------------------------------------------------- /* void TVectorImage::addSeed(const TPointD& p, const TPixel& color) { m_imp->m_seeds.push_back(TFillSeed(color, p, NULL)); } */ //----------------------------------------------------------------------------- UINT TVectorImage::getRegionCount() const { // assert( m_imp->m_areValidRegions || m_imp->m_regions.empty()); return m_imp->m_regions.size(); } //----------------------------------------------------------------------------- TRegion *TVectorImage::getRegion(UINT index) const { assert(index < m_imp->m_regions.size()); // assert( m_imp->m_areValidRegions ); return m_imp->m_regions[index]; } //----------------------------------------------------------------------------- TRegion *TVectorImage::getRegion(TRegionId regId) const { int index = getStrokeIndexById(regId.m_strokeId); assert(m_imp->m_areValidRegions); TRegion *reg = m_imp->getRegion(regId, index); // assert( reg ); return reg; } //----------------------------------------------------------------------------- TRegion *TVectorImage::Imp::getRegion(TRegionId regId, int index) const { assert(index != -1); if (index == -1) return 0; assert(index < (int)m_strokes.size()); if (index >= (int)m_strokes.size()) return 0; std::list &edgeList = m_strokes[index]->m_edgeList; std::list::iterator endList = edgeList.end(); double w0; double w1; for (std::list::iterator it = edgeList.begin(); it != endList; ++it) { w0 = (*it)->m_w0; w1 = (*it)->m_w1; if (w0 < w1) { if (w0 < regId.m_midW && regId.m_midW < w1 && regId.m_direction) return (*it)->m_r; } else { if (w1 < regId.m_midW && regId.m_midW < w0 && !regId.m_direction) return (*it)->m_r; } } #ifdef _DEBUG TPointD cp1 = m_strokes[index]->m_s->getControlPoint(0); TPointD cp2 = m_strokes[index]->m_s->getControlPoint( m_strokes[index]->m_s->getControlPointCount() - 1); #endif return 0; } /* TRegion* TVectorImage::getRegion(TRegionId regId) const { int index = getStrokeIndexById(regId.m_strokeId); assert(index!=-1); if( index == -1 ) return 0; assert( index < (int)m_imp->m_strokes.size() ); if( index >= (int)m_imp->m_strokes.size() ) return 0; std::list &edgeList = m_imp->m_strokes[index]->m_edgeList; std::list::iterator endList = edgeList.end(); double w0; double w1; for(list::iterator it= edgeList.begin(); it!=endList; ++it) { w0 = (*it)->m_w0; w1 = (*it)->m_w1; if(w0m_r; } else { if( w1 < regId.m_midW && regId.m_midW < w0 && !regId.m_direction ) return (*it)->m_r; } } return 0; } */ //----------------------------------------------------------------------------- void TVectorImage::setEdgeColors(int strokeIndex, int leftColorIndex, int rightColorIndex) { std::list &ll = m_imp->m_strokes[strokeIndex]->m_edgeList; std::list::const_iterator l = ll.begin(); std::list::const_iterator l_e = ll.end(); for (; l != l_e; ++l) { // double w0 = (*l)->m_w0, w1 = (*l)->m_w1; if ((*l)->m_w0 > (*l)->m_w1) { if (leftColorIndex != -1) (*l)->m_styleId = leftColorIndex; else if (rightColorIndex != -1) (*l)->m_styleId = rightColorIndex; } else { if (rightColorIndex != -1) (*l)->m_styleId = rightColorIndex; else if (leftColorIndex != -1) (*l)->m_styleId = leftColorIndex; } } } //----------------------------------------------------------------------------- TStroke *TVectorImage::getStroke(UINT index) const { assert(index < m_imp->m_strokes.size()); return m_imp->m_strokes[index]->m_s; } VIStroke *TVectorImage::getVIStroke(UINT index) const { assert(index < m_imp->m_strokes.size()); return m_imp->m_strokes[index]; } //----------------------------------------------------------------------------- VIStroke *TVectorImage::getStrokeById(int id) const { int n = m_imp->m_strokes.size(); for (int i = 0; i < n; i++) if (m_imp->m_strokes[i]->m_s->getId() == id) return m_imp->m_strokes[i]; return 0; } //----------------------------------------------------------------------------- int TVectorImage::getStrokeIndexById(int id) const { int n = m_imp->m_strokes.size(); for (int i = 0; i < n; i++) if (m_imp->m_strokes[i]->m_s->getId() == id) return i; return -1; } //----------------------------------------------------------------------------- int TVectorImage::getStrokeIndex(TStroke *stroke) const { int n = m_imp->m_strokes.size(); for (int i = 0; i < n; i++) if (m_imp->m_strokes[i]->m_s == stroke) return i; return -1; } //----------------------------------------------------------------------------- TRectD TVectorImage::getBBox() const { UINT strokeCount = m_imp->m_strokes.size(); if (strokeCount == 0) return TRectD(); TPalette *plt = getPalette(); TRectD bbox; for (UINT i = 0; i < strokeCount; ++i) { TRectD r = m_imp->m_strokes[i]->m_s->getBBox(); TColorStyle *style = 0; if (plt) style = plt->getStyle(m_imp->m_strokes[i]->m_s->getStyle()); if (dynamic_cast(style) || dynamic_cast( style)) // con i pattern style, il render a volte taglia sulla bbox // dello stroke.... // aumento la bbox della meta' delle sue dimensioni:pezzaccia. r = r.enlarge(std::max(r.getLx() * 0.25, r.getLy() * 0.25)); bbox = ((i == 0) ? r : bbox + r); } return bbox; } //----------------------------------------------------------------------------- bool TVectorImage::getNearestStroke(const TPointD &p, double &outW, UINT &strokeIndex, double &dist2, bool onlyInCurrentGroup) const { dist2 = (std::numeric_limits::max)(); strokeIndex = getStrokeCount(); outW = -1; double tempdis2, tempPar; for (int i = 0; i < (int)m_imp->m_strokes.size(); ++i) { if (onlyInCurrentGroup && !inCurrentGroup(i)) continue; TStroke *s = m_imp->m_strokes[i]->m_s; tempPar = s->getW(p); tempdis2 = tdistance2(TThickPoint(p, 0), s->getThickPoint(tempPar)); if (tempdis2 < dist2) { outW = tempPar; dist2 = tempdis2; strokeIndex = i; } } return dist2 < (std::numeric_limits::max)(); } //----------------------------------------------------------------------------- #if defined(LINUX) || defined(MACOSX) void TVectorImage::render(const TVectorRenderData &rd, TRaster32P &ras) { // hardRenderVectorImage(rd,ras,this); } #endif //----------------------------------------------------------------------------- //#include "timage_io.h" TRaster32P TVectorImage::render(bool onlyStrokes) { TRect bBox = convert(getBBox()); if (bBox.isEmpty()) return (TRaster32P)0; std::unique_ptr offlineGlContext(new TOfflineGL(bBox.getSize())); offlineGlContext->clear(TPixel32(0, 0, 0, 0)); offlineGlContext->makeCurrent(); TVectorRenderData rd(TTranslation(-convert(bBox.getP00())), TRect(bBox.getSize()), getPalette(), 0, true, true); rd.m_drawRegions = !onlyStrokes; offlineGlContext->draw(this, rd, false); return offlineGlContext->getRaster()->clone(); // hardRenderVectorImage(rd,ras,this); } //----------------------------------------------------------------------------- TRegion *TVectorImage::getRegion(const TPointD &p) { #ifndef NEW_REGION_FILL if (!isComputedRegionAlmostOnce()) return 0; if (!m_imp->m_areValidRegions) m_imp->computeRegions(); #endif return m_imp->getRegion(p); } //----------------------------------------------------------------------------- TRegion *TVectorImage::Imp::getRegion(const TPointD &p) { int strokeIndex = (int)m_strokes.size() - 1; while (strokeIndex >= 0) { for (UINT regionIndex = 0; regionIndex < m_regions.size(); regionIndex++) if (areDifferentGroup(strokeIndex, false, regionIndex, true) == -1 && m_regions[regionIndex]->contains(p)) return m_regions[regionIndex]->getRegion(p); int curr = strokeIndex; while (strokeIndex >= 0 && areDifferentGroup(curr, false, strokeIndex, false) == -1) strokeIndex--; } return 0; } //----------------------------------------------------------------------------- int TVectorImage::fillStrokes(const TPointD &p, int styleId) { UINT index; double outW, dist2; if (getNearestStroke(p, outW, index, dist2, true)) { double thick = getStroke(index)->getThickPoint(outW).thick * 1.25; if (thick < 0.5) thick = 0.5; if (dist2 > thick * thick) return -1; assert(index >= 0 && index < m_imp->m_strokes.size()); int ret = m_imp->m_strokes[index]->m_s->getStyle(); m_imp->m_strokes[index]->m_s->setStyle(styleId); return ret; } return -1; } //----------------------------------------------------------------------------- #ifdef NEW_REGION_FILL void TVectorImage::resetRegionFinder() { m_imp->resetRegionFinder(); } #else //------------------------------------------------------------------ int TVectorImage::fill(const TPointD &p, int newStyleId, bool onlyEmpty) { TRegion *r = getRegion(p); if (onlyEmpty && r && r->getStyle() != 0) return -1; if (!m_imp->m_areValidRegions) m_imp->computeRegions(); return m_imp->fill(p, newStyleId); } #endif //----------------------------------------------------------------------------- /* void TVectorImage::autoFill(int styleId) { m_imp->autoFill(styleId, true); } void TVectorImage::Imp::autoFill(int styleId, bool oddLevel) { if (!m_areValidRegions) computeRegions(); for (UINT i = 0; isetStyle(styleId); m_regions[i]->autoFill(styleId, !oddLevel); } } */ //----------------------------------------------------------------------------- /* void TRegion::autoFill(int styleId, bool oddLevel) { for (UINT i = 0; isetStyle(styleId); r->autoFill(styleId, !oddLevel); } } */ //----------------------------------------------------------------------------- int TVectorImage::Imp::fill(const TPointD &p, int styleId) { int strokeIndex = (int)m_strokes.size() - 1; while (strokeIndex >= 0) { if (!inCurrentGroup(strokeIndex)) { strokeIndex--; continue; } for (UINT regionIndex = 0; regionIndex < m_regions.size(); regionIndex++) if (areDifferentGroup(strokeIndex, false, regionIndex, true) == -1 && m_regions[regionIndex]->contains(p)) return m_regions[regionIndex]->fill(p, styleId); int curr = strokeIndex; while (strokeIndex >= 0 && areDifferentGroup(curr, false, strokeIndex, false) == -1) strokeIndex--; } return -1; } //----------------------------------------------------------------------------- bool TVectorImage::selectFill(const TRectD &selArea, TStroke *s, int newStyleId, bool onlyUnfilled, bool fillAreas, bool fillLines) { if (!m_imp->m_areValidRegions) m_imp->computeRegions(); return m_imp->selectFill(selArea, s, newStyleId, onlyUnfilled, fillAreas, fillLines); } //----------------------------------------------------------------------------- bool TVectorImage::Imp::selectFill(const TRectD &selArea, TStroke *s, int newStyleId, bool onlyUnfilled, bool fillAreas, bool fillLines) { bool hitSome = false; if (s) { TVectorImage aux; aux.addStroke(s); aux.findRegions(); for (UINT j = 0; j < aux.getRegionCount(); j++) { TRegion *r0 = aux.getRegion(j); if (fillAreas) for (UINT i = 0; i < m_regions.size(); i++) { TRegion *r1 = m_regions[i]; if (m_insideGroup != TGroupId() && !m_insideGroup.isParentOf( m_strokes[r1->getEdge(0)->m_index]->m_groupId)) continue; if ((!onlyUnfilled || r1->getStyle() == 0) && r0->contains(*r1)) { r1->setStyle(newStyleId); hitSome = true; } } if (fillLines) for (UINT i = 0; i < m_strokes.size(); i++) { if (!inCurrentGroup(i)) continue; TStroke *s1 = m_strokes[i]->m_s; if ((!onlyUnfilled || s1->getStyle() == 0) && r0->contains(*s1)) { s1->setStyle(newStyleId); hitSome = true; } } } aux.removeStroke(0); return hitSome; } // rect fill if (fillAreas) #ifndef NEW_REGION_FILL for (UINT i = 0; i < m_regions.size(); i++) { int index, j = 0; do index = m_regions[i]->getEdge(j++)->m_index; while (index < 0 && j < (int)m_regions[i]->getEdgeCount()); // if index<0, means that the region is purely of autoclose strokes! if (m_insideGroup != TGroupId() && index >= 0 && !m_insideGroup.isParentOf(m_strokes[index]->m_groupId)) continue; if (!onlyUnfilled || m_regions[i]->getStyle() == 0) hitSome |= m_regions[i]->selectFill(selArea, newStyleId); } #else findRegions(selArea); for (UINT i = 0; i < m_regions.size(); i++) { if (m_insideGroup != TGroupId() && !m_insideGroup.isParentOf( m_strokes[m_regions[i]->getEdge(0)->m_index]->m_groupId)) continue; if (!onlyUnfilled || m_regions[i]->getStyle() == 0) hitSome |= m_regions[i]->selectFill(selArea, newStyleId); } #endif if (fillLines) for (UINT i = 0; i < m_strokes.size(); i++) { if (!inCurrentGroup(i)) continue; TStroke *s = m_strokes[i]->m_s; if ((!onlyUnfilled || s->getStyle() == 0) && selArea.contains(s->getBBox())) { s->setStyle(newStyleId); hitSome = true; } } return hitSome; } //----------------------------------------------------------------------------- /* void TVectorImageImp::seedFill() { std::vector::iterator it; TRegion*r; TFillStyleP app =NULL; for (it=m_seeds.begin(); it!=m_seeds.end(); ) if ((r=fill(it->m_p, new TColorStyle(it->m_fillStyle.getPointer()) ))!=NULL) { it->m_r = r; it = m_seeds.erase(it); // i seed provengono da immagini vecchie. non servono piu'. } m_areValidRegions=true; } */ //----------------------------------------------------------------------------- /* void TVectorImage::seedFill() { m_imp->seedFill(); } */ //----------------------------------------------------------------------------- void TVectorImage::notifyChangedStrokes( const std::vector &strokeIndexArray, const std::vector &oldStrokeArray, bool areFlipped) { std::vector aux; /* if (oldStrokeArray.empty()) { for (int i=0; i<(int)strokeIndexArray.size(); i++) { TStroke *s = getStroke(strokeIndexArray[i]); aux.push_back(s); } m_imp->notifyChangedStrokes(strokeIndexArray, aux, areFlipped); } else*/ m_imp->notifyChangedStrokes(strokeIndexArray, oldStrokeArray, areFlipped); } //----------------------------------------------------------------------------- void TVectorImage::notifyChangedStrokes(int strokeIndexArray, TStroke *oldStroke, bool isFlipped) { std::vector app(1); app[0] = strokeIndexArray; std::vector oldStrokeArray(1); oldStrokeArray[0] = oldStroke ? oldStroke : getStroke(strokeIndexArray); m_imp->notifyChangedStrokes(app, oldStrokeArray, isFlipped); } //----------------------------------------------------------------------------- // ofstream of("C:\\temp\\butta.txt"); void transferColors(const std::list &oldList, const std::list &newList, bool isStrokeChanged, bool isFlipped, bool overwriteColor) { if (newList.empty() || oldList.empty()) return; std::list::const_iterator it; // unused variable #if 0 list::const_iterator it1; #endif double totLength; if (isStrokeChanged) totLength = newList.front()->m_s->getLength(); for (it = newList.begin(); it != newList.end(); ++it) { int newStyle = -1; // ErrorStyle; // unused variable #if 0 int styleId = (*it)->m_styleId; #endif if (!overwriteColor && (*it)->m_styleId != 0) continue; bool reversed; double deltaMax = 0.005; double l0, l1; if ((*it)->m_w0 > (*it)->m_w1) { reversed = !isFlipped; if (isStrokeChanged) { l0 = (*it)->m_s->getLength((*it)->m_w1) / totLength; l1 = (*it)->m_s->getLength((*it)->m_w0) / totLength; } else { l0 = (*it)->m_w1; l1 = (*it)->m_w0; } } else { reversed = isFlipped; if (isStrokeChanged) { l0 = (*it)->m_s->getLength((*it)->m_w0) / totLength; l1 = (*it)->m_s->getLength((*it)->m_w1) / totLength; } else { l0 = (*it)->m_w0; l1 = (*it)->m_w1; } // w0 = (*it)->m_w0; // w1 = (*it)->m_w1; } std::list::const_iterator it1 = oldList.begin(); for (; it1 != oldList.end(); ++it1) { // unused variable #if 0 TEdge*e = *it1; #endif if (/*(*it1)->m_styleId==0 ||*/ (reversed && (*it1)->m_w0 < (*it1)->m_w1) || (!reversed && (*it1)->m_w0 > (*it1)->m_w1)) continue; double _l0, _l1; if (isStrokeChanged) { double totLength1 = (*it1)->m_s->getLength(); _l0 = (*it1)->m_s->getLength(std::min((*it1)->m_w0, (*it1)->m_w1)) / totLength1; _l1 = (*it1)->m_s->getLength(std::max((*it1)->m_w0, (*it1)->m_w1)) / totLength1; } else { _l0 = std::min((*it1)->m_w0, (*it1)->m_w1); _l1 = std::max((*it1)->m_w0, (*it1)->m_w1); } double delta = std::min(l1, _l1) - std::max(l0, _l0); if (delta > deltaMax) { deltaMax = delta; newStyle = (*it1)->m_styleId; } } if (newStyle >= 0) // !=ErrorStyle) { if ((*it)->m_r) (*it)->m_r->setStyle(newStyle); else (*it)->m_styleId = newStyle; } } } //----------------------------------------------------------------------------- void TVectorImage::transferStrokeColors(TVectorImageP sourceImage, int sourceStroke, TVectorImageP destinationImage, int destinationStroke) { std::list *sourceList = &(sourceImage->m_imp->m_strokes[sourceStroke]->m_edgeList); std::list *destinationList = &(destinationImage->m_imp->m_strokes[destinationStroke]->m_edgeList); transferColors(*sourceList, *destinationList, true, false, false); } //----------------------------------------------------------------------------- bool TVectorImage::Imp::areWholeGroups(const std::vector &indexes) const { UINT i, j; for (i = 0; i < indexes.size(); i++) { if (m_strokes[indexes[i]]->m_isNewForFill) return false; if (!m_strokes[indexes[i]]->m_groupId.isGrouped()) return false; for (j = 0; j < m_strokes.size(); j++) { int ret = areDifferentGroup(indexes[i], false, j, false); if (ret == -1 || (ret >= 1 && find(indexes.begin(), indexes.end(), j) == indexes.end())) return false; } } return true; } //----------------------------------------------------------------------------- //------------------------------------------------------------------- void TVectorImage::Imp::notifyChangedStrokes( const std::vector &strokeIndexArray, const std::vector &oldStrokeArray, bool areFlipped) { #ifdef _DEBUG checkIntersections(); #endif assert(oldStrokeArray.empty() || strokeIndexArray.size() == oldStrokeArray.size()); if (!m_computedAlmostOnce && !m_notIntersectingStrokes) return; typedef std::list EdgeList; std::vector oldEdgeListArray(strokeIndexArray.size()); int i; // se si sono trasformati interi gruppi (senza deformare le stroke) non c'e' // bisogno di ricalcolare le regioni! if (oldStrokeArray.empty() && areWholeGroups(strokeIndexArray)) { m_areValidRegions = true; for (i = 0; i < (int)m_regions.size(); i++) invalidateRegionPropAndBBox(m_regions[i]); return; } QMutexLocker sl(m_mutex); for (i = 0; i < (int)strokeIndexArray.size(); i++) // ATTENZIONE! non si puo' // fare eraseIntersection // in questo stesso ciclo { VIStroke *s = m_strokes[strokeIndexArray[i]]; // if (s->m_s->isSelfLoop()) // assert(s->m_edgeList.size()<=1); std::list::iterator it = s->m_edgeList.begin(); for (; it != s->m_edgeList.end(); it++) { TEdge *e = new TEdge(**it, false); if (!oldStrokeArray.empty()) e->m_s = oldStrokeArray[i]; oldEdgeListArray[i].push_back(e); // bisogna allocare nuovo edge, // perche'la eraseIntersection poi lo // cancella.... if ((*it)->m_toBeDeleted) delete *it; } s->m_edgeList.clear(); } for (i = 0; i < (int)strokeIndexArray.size(); i++) { eraseIntersection(strokeIndexArray[i]); if (!m_notIntersectingStrokes) m_strokes[strokeIndexArray[i]]->m_isNewForFill = true; } computeRegions(); // m_imp->m_strokes, m_imp->m_regions); for (i = 0; i < (int)strokeIndexArray.size(); i++) { transferColors(oldEdgeListArray[i], m_strokes[strokeIndexArray[i]]->m_edgeList, true, areFlipped, false); clearPointerContainer(oldEdgeListArray[i]); } #ifdef _DEBUG checkIntersections(); #endif } //----------------------------------------------------------------------------- void TVectorImage::findRegions() { // for (int i=0; i<(int)m_imp->m_strokes.size(); i++) // { // m_imp->eraseIntersection(i); // m_imp->m_strokes[i]->m_isNewForFill=true; // } if (m_imp->m_areValidRegions) return; // m_imp->m_regions.clear(); // compute regions... m_imp->computeRegions(); // m_imp->m_strokes, m_imp->m_regions); } //----------------------------------------------------------------------------- void TVectorImage::putRegion(TRegion *region) { m_imp->m_regions.push_back(region); } //----------------------------------------------------------------------------- //------------------------------------------------------------------- void TVectorImage::Imp::cloneRegions(TVectorImage::Imp &out, bool doComputeRegions) { std::unique_ptr v; UINT size = getFillData(v); out.setFillData(v, size, doComputeRegions); } //----------------------------------------------------------------------------- TVectorImageP TVectorImage::clone() const { return TVectorImageP(cloneImage()); } //----------------------------------------------------------------------------- TImage *TVectorImage::cloneImage() const { TVectorImage *out = new TVectorImage; out->m_imp->m_autocloseTolerance = m_imp->m_autocloseTolerance; out->m_imp->m_maxGroupId = m_imp->m_maxGroupId; out->m_imp->m_maxGhostGroupId = m_imp->m_maxGhostGroupId; for (int i = 0; i < (int)m_imp->m_strokes.size(); i++) { out->m_imp->m_strokes.push_back(new VIStroke(*(m_imp->m_strokes[i]))); out->m_imp->m_strokes.back()->m_s->setId(m_imp->m_strokes[i]->m_s->getId()); } m_imp->cloneRegions(*out->m_imp); out->setPalette(getPalette()); out->m_imp->m_computedAlmostOnce = m_imp->m_computedAlmostOnce; out->m_imp->m_justLoaded = m_imp->m_justLoaded; return out; } //----------------------------------------------------------------------------- /* TVectorImageP mergeAndClear(TVectorImageP v1, TVectorImageP v2 ) { TVectorImageP out = new TVectorImage; std::vector::iterator it_b = v1->m_imp->m_strokes.begin(); std::vector::iterator it_e = v1->m_imp->m_strokes.end(); std::copy( it_b, it_e, std::back_inserter( out->m_imp->m_strokes ) ); it_b = v2->m_imp->m_strokes.begin(); it_e = v2->m_imp->m_strokes.end(); std::copy( it_b, it_e, std::back_inserter( out->m_imp->m_strokes ) ); v1->m_imp->m_regions.clear(); v1->m_imp->m_strokes.clear(); v2->m_imp->m_regions.clear(); v2->m_imp->m_strokes.clear(); out->m_imp->m_areValidRegions = false; return out; } */ //----------------------------------------------------------------------------- VIStroke::VIStroke(const VIStroke &s, bool sameId) : m_isPoint(s.m_isPoint) , m_isNewForFill(s.m_isNewForFill) , m_groupId(s.m_groupId) { m_s = new TStroke(*s.m_s); std::list::const_iterator it = s.m_edgeList.begin(), it_e = s.m_edgeList.end(); for (; it != it_e; ++it) { m_edgeList.push_back(new TEdge(**it, true)); m_edgeList.back()->m_s = m_s; } if (sameId) m_s->setId(s.m_s->getId()); } //----------------------------------------------------------------------------- void TVectorImage::mergeImage(const TVectorImageP &img, const TAffine &affine, bool sameStrokeId) { QMutexLocker sl(m_imp->m_mutex); #ifdef _DEBUG checkIntersections(); #endif TPalette *tarPlt = getPalette(); TPalette *srcPlt = img->getPalette(); assert(tarPlt); assert(tarPlt->getPageCount() > 0); // merge della palette std::map styleTable; std::set usedStyles; img->getUsedStyles(usedStyles); // gmt, 16/10/07. Quando si copia e incolla un path su uno stroke succede // che la palette dell'immagine sorgente sia vuota. Non mi sembra sbagliato // mettere comunque un test qui if (srcPlt) mergePalette(tarPlt, styleTable, srcPlt, usedStyles); mergeImage(img, affine, styleTable, sameStrokeId); } //----------------------------------------------------------------------------- void TVectorImage::mergeImage(const TVectorImageP &img, const TAffine &affine, const std::map &styleTable, bool sameStrokeId) { int imageSize = img->getStrokeCount(); if (imageSize == 0) return; QMutexLocker sl(m_imp->m_mutex); m_imp->m_computedAlmostOnce |= img->m_imp->m_computedAlmostOnce; std::vector changedStrokeArray(imageSize); img->m_imp->reindexGroups(*m_imp); int i; int insertAt = 0; if (m_imp->m_insideGroup != TGroupId()) // if is inside a group, new image is put in that group. { TGroupId groupId; for (i = m_imp->m_strokes.size() - 1; i >= 0; i--) { if (m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[i]->m_groupId)) { insertAt = i + 1; groupId = m_imp->m_strokes[i]->m_groupId; break; } } if (insertAt != 0) { for (i = 0; i < (int)img->m_imp->m_strokes.size(); i++) { if (!img->m_imp->m_strokes[i]->m_groupId.isGrouped()) { img->m_imp->m_strokes[i]->m_groupId = groupId; } else { img->m_imp->m_strokes[i]->m_groupId = TGroupId(groupId, img->m_imp->m_strokes[i]->m_groupId); } } } } // si fondono l'ultimo gruppo ghost della vecchia a e il primo della nuova else if (!m_imp->m_strokes.empty() && m_imp->m_strokes.back()->m_groupId.isGrouped(true) != 0 && img->m_imp->m_strokes[0]->m_groupId.isGrouped(true) != 0) { TGroupId idNew = m_imp->m_strokes.back()->m_groupId, idOld = img->m_imp->m_strokes[0]->m_groupId; for (i = 0; i < (int)img->m_imp->m_strokes.size() && img->m_imp->m_strokes[i]->m_groupId == idOld; i++) img->m_imp->m_strokes[i]->m_groupId = idNew; } // merge dell'immagine std::map::const_iterator styleTableIt; int oldSize = getStrokeCount(); for (i = 0; i < imageSize; i++) { VIStroke *srcStroke = img->m_imp->m_strokes[i]; VIStroke *tarStroke = new VIStroke(*srcStroke, sameStrokeId); int styleId; // cambio i colori delle regioni std::list::const_iterator it = tarStroke->m_edgeList.begin(), it_e = tarStroke->m_edgeList.end(); for (; it != it_e; ++it) { int styleId = (*it)->m_styleId; styleTableIt = styleTable.find(styleId); assert(styleTableIt != styleTable.end()); if (styleTableIt != styleTable.end()) (*it)->m_styleId = styleTableIt->second; } tarStroke->m_s->transform(affine, true); int strokeId = srcStroke->m_s->getId(); if (getStrokeById(strokeId) == 0) tarStroke->m_s->setId(strokeId); // cambio i colori dello stroke styleId = srcStroke->m_s->getStyle(); styleTableIt = styleTable.find(styleId); assert(styleTableIt != styleTable.end()); if (styleTableIt != styleTable.end()) tarStroke->m_s->setStyle(styleTableIt->second); if (insertAt == 0) { m_imp->m_strokes.push_back(tarStroke); changedStrokeArray[i] = oldSize + i; } else { std::vector::iterator it = m_imp->m_strokes.begin(); advance(it, insertAt + i); m_imp->m_strokes.insert(it, tarStroke); changedStrokeArray[i] = insertAt + i; } } if (insertAt > 0) { // for (i=changedStrokeArray.back()+1; im_strokes.size(); i++) // changedStrokeArray.push_back(i); m_imp->reindexEdges(changedStrokeArray, true); } notifyChangedStrokes(changedStrokeArray, std::vector(), false); #ifdef _DEBUG checkIntersections(); #endif } //----------------------------------------------------------------------------- void TVectorImage::Imp::reindexGroups(TVectorImage::Imp &img) { UINT i, j; int newMax = img.m_maxGroupId; int newMaxGhost = img.m_maxGhostGroupId; for (i = 0; i < m_strokes.size(); i++) { VIStroke *s = m_strokes[i]; if (s->m_groupId.m_id.empty()) continue; if (s->m_groupId.m_id[0] > 0) for (j = 0; j < s->m_groupId.m_id.size(); j++) { s->m_groupId.m_id[j] += img.m_maxGroupId; newMax = std::max(newMax, s->m_groupId.m_id[j]); } else for (j = 0; j < s->m_groupId.m_id.size(); j++) { s->m_groupId.m_id[j] -= img.m_maxGhostGroupId; newMaxGhost = std::max(newMaxGhost, -s->m_groupId.m_id[j]); } } m_maxGroupId = img.m_maxGroupId = newMax; m_maxGhostGroupId = img.m_maxGhostGroupId = newMaxGhost; } //----------------------------------------------------------------------------- void TVectorImage::mergeImage(const std::vector &images) { UINT oldSize = getStrokeCount(); std::vector changedStrokeArray; const TVectorImage *img; int index; if (m_imp->m_insideGroup != TGroupId()) { for (index = m_imp->m_strokes.size() - 1; index > -1; index--) if (m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[index]->m_groupId)) break; assert(index > -1); } else index = getStrokeCount() - 1; for (UINT j = 0; j < images.size(); ++j) { img = images[j]; if (img->getStrokeCount() == 0) continue; img->m_imp->reindexGroups(*m_imp); int i = 0; /*if (!m_imp->m_strokes.empty() && m_imp->m_strokes[index-1]->m_groupId.isGrouped(true)!=0 && img->m_imp->m_strokes[0]->m_groupId.isGrouped(true)!=0) { assert(false); TGroupId idNew = m_imp->m_strokes[index]->m_groupId, idOld = img->m_imp->m_strokes[0]->m_groupId; for (;i<(int)img->m_imp->m_strokes.size() && img->m_imp->m_strokes[i]->m_groupId==idOld; i++) img->m_imp->m_strokes[i]->m_groupId==idNew; }*/ int strokeCount = img->getStrokeCount(); m_imp->m_computedAlmostOnce |= img->m_imp->m_computedAlmostOnce; for (i = 0; i < strokeCount; i++) { VIStroke *srcStroke = img->m_imp->m_strokes[i]; VIStroke *tarStroke = new VIStroke(*srcStroke); int strokeId = srcStroke->m_s->getId(); if (getStrokeById(strokeId) == 0) tarStroke->m_s->setId(strokeId); index++; if (m_imp->m_insideGroup == TGroupId()) m_imp->m_strokes.push_back(tarStroke); else // if we are inside a group, the images must become part of that // group { tarStroke->m_groupId = TGroupId(m_imp->m_insideGroup, tarStroke->m_groupId); m_imp->insertStrokeAt(tarStroke, index); } changedStrokeArray.push_back(index); } } notifyChangedStrokes(changedStrokeArray, std::vector(), false); } //------------------------------------------------------------------- void TVectorImage::recomputeRegionsIfNeeded() { if (!m_imp->m_justLoaded) return; m_imp->m_justLoaded = false; std::vector v(m_imp->m_strokes.size()); int i; for (i = 0; i < (int)m_imp->m_strokes.size(); i++) v[i] = i; m_imp->notifyChangedStrokes(v, std::vector(), false); } //----------------------------------------------------------------------------- void TVectorImage::eraseStyleIds(const std::vector styleIds) { int j; for (j = 0; j < (int)styleIds.size(); j++) { int styleId = styleIds[j]; int strokeCount = getStrokeCount(); int i; for (i = strokeCount - 1; i >= 0; i--) { TStroke *stroke = getStroke(i); if (stroke && stroke->getStyle() == styleId) removeStroke(i); } int regionCount = getRegionCount(); for (i = 0; i < regionCount; i++) { TRegion *region = getRegion(i); if (!region || region->getStyle() != styleId) continue; TPointD p; if (region->getInternalPoint(p)) fill(p, 0); } } } //------------------------------------------------------------------- void TVectorImage::insertImage(const TVectorImageP &img, const std::vector &dstIndices) { UINT i; UINT imageSize = img->getStrokeCount(); assert(dstIndices.size() == imageSize); // img->m_imp->reindexGroups(*m_imp); std::vector changedStrokeArray(imageSize); std::vector::iterator it = m_imp->m_strokes.begin(); for (i = 0; i < imageSize; i++) { assert(i == 0 || dstIndices[i] > dstIndices[i - 1]); VIStroke *srcStroke = img->m_imp->m_strokes[i]; VIStroke *tarStroke = new VIStroke(*srcStroke); int strokeId = srcStroke->m_s->getId(); if (getStrokeById(strokeId) == 0) tarStroke->m_s->setId(strokeId); advance(it, (i == 0) ? dstIndices[i] : dstIndices[i] - dstIndices[i - 1]); it = m_imp->m_strokes.insert(it, tarStroke); changedStrokeArray[i] = dstIndices[i]; } m_imp->reindexEdges(changedStrokeArray, true); notifyChangedStrokes(changedStrokeArray, std::vector(), false); // m_imp->computeRegions(); } //----------------------------------------------------------------------------- void TVectorImage::enableRegionComputing(bool enabled, bool notIntersectingStrokes) { m_imp->m_computeRegions = enabled; m_imp->m_notIntersectingStrokes = notIntersectingStrokes; } //------------------------------------------------------------------------------ void TVectorImage::enableMinimizeEdges(bool enabled) { m_imp->m_minimizeEdges = enabled; } //----------------------------------------------------------------------------- TVectorImageP TVectorImage::splitImage(const std::vector &indices, bool removeFlag) { TVectorImageP out = new TVectorImage; out->m_imp->m_maxGroupId = m_imp->m_maxGroupId; out->m_imp->m_maxGhostGroupId = m_imp->m_maxGhostGroupId; std::vector toBeRemoved; TPalette *vp = getPalette(); if (vp) out->setPalette(vp->clone()); for (UINT i = 0; i < indices.size(); ++i) { VIStroke *ref = m_imp->m_strokes[indices[i]]; assert(ref); VIStroke *vs = new VIStroke(*ref); vs->m_isNewForFill = true; out->m_imp->m_strokes.push_back(vs); } if (removeFlag) removeStrokes(indices, true, true); out->m_imp->m_areValidRegions = false; out->m_imp->m_computedAlmostOnce = m_imp->m_computedAlmostOnce; return out; } //----------------------------------------------------------------------------- TVectorImageP TVectorImage::splitSelected(bool removeFlag) { TVectorImageP out = new TVectorImage; std::vector toBeRemoved; for (UINT i = 0; i < getStrokeCount(); ++i) { VIStroke *ref = m_imp->m_strokes[i]; assert(ref); if (ref->m_s->getFlag(TStroke::c_selected_flag)) { VIStroke *stroke = new VIStroke(*ref); out->m_imp->m_strokes.push_back(stroke); if (removeFlag) { toBeRemoved.push_back(i); // removeStroke(i); // delete ref; // i--; } } } removeStrokes(toBeRemoved, true, true); out->m_imp->m_areValidRegions = false; return out; } //----------------------------------------------------------------------------- void TVectorImage::validateRegions(bool state) { m_imp->m_areValidRegions = state; } //----------------------------------------------------------------------------- /* void TVectorImage::invalidateBBox() { for(UINT i=0; iinvalidateBBox(); } */ //----------------------------------------------------------------------------- void TVectorImage::setFillData(std::unique_ptr const &v, UINT branchCount, bool doComputeRegions) { m_imp->setFillData(v, branchCount, doComputeRegions); } //----------------------------------------------------------------------------- UINT TVectorImage::getFillData(std::unique_ptr &v) { return m_imp->getFillData(v); } //----------------------------------------------------------------------------- void TVectorImage::enableStrokeStyle(int index, bool enable) { DisabledStrokeStyles &disabledSet = getDisabledStrokeStyleSet(); if (enable) disabledSet.erase(index); else disabledSet.insert(index); } //----------------------------------------------------------------------------- bool TVectorImage::isStrokeStyleEnabled(int index) { return isStrokeStyleEnabled__(index); } //----------------------------------------------------------------------------- void TVectorImage::getUsedStyles(std::set &styles) const { UINT strokeCount = getStrokeCount(); UINT i = 0; for (; i < strokeCount; ++i) { VIStroke *srcStroke = m_imp->m_strokes[i]; int styleId = srcStroke->m_s->getStyle(); if (styleId != 0) styles.insert(styleId); std::list::const_iterator it = srcStroke->m_edgeList.begin(); for (; it != srcStroke->m_edgeList.end(); ++it) { styleId = (*it)->m_styleId; if (styleId != 0) styles.insert(styleId); } } } //----------------------------------------------------------------------------- inline double recomputeW1(double oldW, const TStroke &oldStroke, const TStroke &newStroke, double startW) { double oldLength = oldStroke.getLength(); double newLength = newStroke.getLength(); assert(startW <= oldW); assert(newLength < oldLength); double s = oldStroke.getLength(startW, oldW); assert(s <= newLength || areAlmostEqual(s, newLength, 1e-5)); return newStroke.getParameterAtLength(s); } //----------------------------------------------------------------------------- inline double recomputeW2(double oldW, const TStroke &oldStroke, const TStroke &newStroke, double length) { double s = oldStroke.getLength(oldW); return newStroke.getParameterAtLength(length + s); } //----------------------------------------------------------------------------- inline double recomputeW(double oldW, const TStroke &oldStroke, const TStroke &newStroke, bool isAtBegin) { double oldLength = oldStroke.getLength(); double newLength = newStroke.getLength(); assert(newLength < oldLength); double s = oldStroke.getLength(oldW) - ((isAtBegin) ? 0 : oldLength - newLength); assert(s <= newLength || areAlmostEqual(s, newLength, 1e-5)); return newStroke.getParameterAtLength(s); } //----------------------------------------------------------------------------- #ifdef _DEBUG void TVectorImage::checkIntersections() { m_imp->checkIntersections(); } #endif /* void TVectorImage::reassignStyles() { set styles; UINT strokeCount = getStrokeCount(); UINT i=0; for( ; i< strokeCount; ++i) { int styleId = getStroke(i)->getStyle(); if(styleId != 0) styles.insert(styleId); } UINT regionCount = getRegionCount(); for( i = 0; i< regionCount; ++i) { int styleId = getRegion(i)->getStyle(); if(styleId != 0) styles.insert(styleId); } map conversionTable; for(set::iterator it = styles.begin(); it != styles.end(); ++it) { int styleId = *it; conversionTable[styleId] = styleId + 13; } for( i = 0; i< strokeCount; ++i) { TStroke *stroke = getStroke(i); int styleId = stroke->getStyle(); if(styleId != 0) { map::iterator it = conversionTable.find(styleId); if(it != conversionTable.end()) stroke->setStyle(it->second); } } for( i = 0; i< regionCount; ++i) { TRegion *region = getRegion(i); int styleId = region->getStyle(); if(styleId != 0) { map::iterator it = conversionTable.find(styleId); if(it != conversionTable.end()) region->setStyle(it->second); } } } */ //----------------------------------------------------------------------------- bool TVectorImage::isComputedRegionAlmostOnce() const { return m_imp->m_computedAlmostOnce; } //----------------------------------------------------------------------------- void TVectorImage::splitStroke(int strokeIndex, const std::vector &sortedWRanges) { m_imp->splitStroke(strokeIndex, sortedWRanges); } void TVectorImage::Imp::splitStroke( int strokeIndex, const std::vector &sortedWRanges) { int i; VIStroke *subV = 0; if (strokeIndex >= (int)m_strokes.size() || sortedWRanges.empty()) return; VIStroke *vs = m_strokes[strokeIndex]; TGroupId groupId = vs->m_groupId; // se e' un self loop, alla fine non lo sara', e deve stare insieme // alle stroke non loopate. sposto lo stroke se serve /* { if (vs->m_s->isSelfLoop()) int up = strokeIndex+1; while (up<(int)m_strokes.size() && m_strokes[up]->m_s->isSelfLoop()) up++; int dn = strokeIndex-1; while (dn>=0 && m_strokes[dn]->m_s->isSelfLoop()) dn--; if ((up == m_strokes.size() || up!=strokeIndex+1) && (dn<0 || dn!=strokeIndex-1)) { if (up>=(int)m_strokes.size()) { assert(dn>=0); moveStroke(strokeIndex, dn+1); strokeIndex = dn+1; } else { moveStroke(strokeIndex, up-1); strokeIndex = up-1; } } } */ assert(vs == m_strokes[strokeIndex]); bool toBeJoined = (vs->m_s->isSelfLoop() && sortedWRanges.front().first == 0.0 && sortedWRanges.back().second == 1.0); int styleId = vs->m_s->getStyle(); TStroke::OutlineOptions oOptions(vs->m_s->outlineOptions()); m_regions.clear(); std::list origEdgeList; // metto al pizzo la edge std::list della // stroke, perche' la erase intersection ne // fara' scempio std::list::iterator it = vs->m_edgeList.begin(), it_e = vs->m_edgeList.end(); for (; it != it_e; ++it) origEdgeList.push_back(new TEdge(**it, false)); removeStroke(strokeIndex, false); std::vector> edgeList(sortedWRanges.size()); strokeIndex--; int wSize = (int)sortedWRanges.size(); for (i = 0; i < wSize; i++) { assert(sortedWRanges[i].first < sortedWRanges[i].second); assert(i == wSize - 1 || sortedWRanges[i].second <= sortedWRanges[i + 1].first); assert(sortedWRanges[i].first >= 0 && sortedWRanges[i].first <= 1); assert(sortedWRanges[i].second >= 0 && sortedWRanges[i].second <= 1); subV = new VIStroke(new TStroke(), groupId); TStroke s, dummy; if (areAlmostEqual(sortedWRanges[i].first, 0, 1e-4)) s = *vs->m_s; else vs->m_s->split(sortedWRanges[i].first, dummy, s); double lenAtW0 = vs->m_s->getLength(sortedWRanges[i].first); double lenAtW1 = vs->m_s->getLength(sortedWRanges[i].second); double newW1 = s.getParameterAtLength(lenAtW1 - lenAtW0); if (areAlmostEqual(newW1, 1.0, 1e-4)) *(subV->m_s) = s; else s.split(newW1, *(subV->m_s), dummy); strokeIndex++; /*assert(m_strokes[strokeIndex]->m_edgeList.empty()); assert(m_strokes[strokeIndex-wSize+1]->m_edgeList.empty());*/ std::list::const_iterator it = origEdgeList.begin(), it_e = origEdgeList.end(); for (; it != it_e; ++it) { double wMin = std::min((*it)->m_w0, (*it)->m_w1); double wMax = std::max((*it)->m_w0, (*it)->m_w1); if (wMin >= sortedWRanges[i].second || wMax <= sortedWRanges[i].first) continue; TEdge *e = new TEdge(**it, false); if (wMin < sortedWRanges[i].first) wMin = 0.0; else wMin = recomputeW1(wMin, *(vs->m_s), *(subV->m_s), sortedWRanges[i].first); if (wMax > sortedWRanges[i].second) wMax = 1.0; else wMax = recomputeW1(wMax, *(vs->m_s), *(subV->m_s), sortedWRanges[i].first); if (e->m_w0 < e->m_w1) e->m_w0 = wMin, e->m_w1 = wMax; else e->m_w1 = wMin, e->m_w0 = wMax; e->m_r = 0; e->m_s = subV->m_s; e->m_index = strokeIndex; edgeList[i].push_back(e); } subV->m_edgeList.clear(); insertStrokeAt(subV, strokeIndex); subV->m_s->setStyle(styleId); subV->m_s->outlineOptions() = oOptions; } clearPointerContainer(origEdgeList); if (toBeJoined) // la stroke e' un loop, quindi i due choncketti iniziali e // finali vanno joinati { VIStroke *s0 = m_strokes[strokeIndex]; VIStroke *s1 = m_strokes[strokeIndex - wSize + 1]; std::list &l0 = edgeList.back(); std::list &l1 = edgeList.front(); // assert(s0->m_edgeList.empty()); // assert(s1->m_edgeList.empty()); removeStroke(strokeIndex - wSize + 1, false); strokeIndex--; removeStroke(strokeIndex, false); VIStroke *s = new VIStroke(joinStrokes(s0->m_s, s1->m_s), groupId); insertStrokeAt(s, strokeIndex); std::list::iterator it = l0.begin(), it_e = l0.end(); for (; it != it_e; ++it) { (*it)->m_s = s->m_s; (*it)->m_index = strokeIndex; (*it)->m_w0 = recomputeW2((*it)->m_w0, *(s0->m_s), *(s->m_s), 0); (*it)->m_w1 = recomputeW2((*it)->m_w1, *(s0->m_s), *(s->m_s), 0); } it = l1.begin(); double length = s0->m_s->getLength(); while (it != l1.end()) { (*it)->m_s = s->m_s; (*it)->m_index = strokeIndex; (*it)->m_w0 = recomputeW2((*it)->m_w0, *(s1->m_s), *(s->m_s), length); (*it)->m_w1 = recomputeW2((*it)->m_w1, *(s1->m_s), *(s->m_s), length); l0.push_back(*it); it = l1.erase(it); } assert(l1.empty()); edgeList.erase(edgeList.begin()); std::vector appSortedWRanges; wSize--; delete s0; delete s1; } // checkIntersections(); // double len = e->m_s->getLength(); // if (recomputeRegions) if (m_computedAlmostOnce) { computeRegions(); assert((int)edgeList.size() == wSize); assert((int)m_strokes.size() > strokeIndex); for (i = 0; i < wSize; i++) transferColors(edgeList[i], m_strokes[strokeIndex - wSize + i + 1]->m_edgeList, false, false, false); } for (i = 0; i < wSize; i++) clearPointerContainer(edgeList[i]); delete vs; } //----------------------------------------------------------------------------- static void computeEdgeList(TStroke *newS, const std::list &edgeList1, bool join1AtBegin, const std::list &edgeList2, bool join2AtBegin, std::list &edgeList) { std::list::const_iterator it; if (!edgeList1.empty()) { TStroke *s1 = edgeList1.front()->m_s; double length1 = s1->getLength(); ; for (it = edgeList1.begin(); it != edgeList1.end(); ++it) { double l0 = s1->getLength((*it)->m_w0), l1 = s1->getLength((*it)->m_w1); if (join1AtBegin) l0 = length1 - l0, l1 = length1 - l1; TEdge *e = new TEdge(); e->m_toBeDeleted = true; e->m_index = -1; e->m_s = newS; e->m_styleId = (*it)->m_styleId; e->m_w0 = newS->getParameterAtLength(l0); e->m_w1 = newS->getParameterAtLength(l1); edgeList.push_back(e); } } if (!edgeList2.empty()) { TStroke *s2 = edgeList2.front()->m_s; double offset = newS->getLength(newS->getW(s2->getPoint(0.0))); double length2 = s2->getLength(); for (it = edgeList2.begin(); it != edgeList2.end(); ++it) { double l0 = s2->getLength((*it)->m_w0), l1 = s2->getLength((*it)->m_w1); if (!join2AtBegin) l0 = length2 - l0, l1 = length2 - l1; TEdge *e = new TEdge(); e->m_toBeDeleted = true; e->m_index = -1; e->m_s = newS; e->m_styleId = (*it)->m_styleId; e->m_w0 = newS->getParameterAtLength(offset + l0); e->m_w1 = newS->getParameterAtLength(offset + l1); edgeList.push_back(e); } } } //----------------------------------------------------------------------------- #ifdef _DEBUG //#include "tpalette.h" #include "tcolorstyles.h" void printEdges(std::ofstream &os, char *str, TPalette *plt, const std::list &edges) { std::list::const_iterator it; os << str << std::endl; for (it = edges.begin(); it != edges.end(); ++it) { TColorStyle *style = plt->getStyle((*it)->m_styleId); TPixel32 color = style->getMainColor(); os << "w0-w1:(" << (*it)->m_w0 << "-->" << (*it)->m_w1 << ")" << std::endl; os << "color=(" << color.r << "," << color.g << "," << color.b << ")" << std::endl; } os << std::endl << std::endl << std::endl; } #else #define printEdges #endif //----------------------------------------------------------------------------- #ifdef _DEBUG void TVectorImage::Imp::printStrokes(std::ofstream &os) { for (int i = 0; i < (int)m_strokes.size(); i++) { os << "*****stroke #" << i << " *****"; m_strokes[i]->m_s->print(os); } } #endif //----------------------------------------------------------------------------- TStroke *TVectorImage::removeEndpoints(int strokeIndex) { return m_imp->removeEndpoints(strokeIndex); } void TVectorImage::restoreEndpoints(int index, TStroke *oldStroke) { m_imp->restoreEndpoints(index, oldStroke); } //----------------------------------------------------------------------------- VIStroke *TVectorImage::Imp::extendStrokeSmoothly(int index, const TThickPoint &pos, int cpIndex) { TStroke *stroke = m_strokes[index]->m_s; TGroupId groupId = m_strokes[index]->m_groupId; int cpCount = stroke->getControlPointCount(); int styleId = stroke->getStyle(); const TThickQuadratic *q = stroke->getChunk(cpIndex == 0 ? 0 : stroke->getChunkCount() - 1); double len = q->getLength(); double w = exp(-len * 0.01); TThickPoint m = q->getThickP1(); TThickPoint p1 = (cpIndex == 0 ? q->getThickP0() : q->getThickP2()) * (1 - w) + m * w; TThickPoint middleP = (p1 + pos) * 0.5; double angle = fabs(cross(normalize(m - middleP), normalize(pos - middleP))); if (angle < 0.05) middleP = (m + pos) * 0.5; stroke->setControlPoint(cpIndex, middleP); if (isAlmostZero(len)) { if (cpIndex == 0) stroke->setControlPoint(1, middleP * 0.1 + stroke->getControlPoint(2) * 0.9); else stroke->setControlPoint( cpCount - 2, middleP * 0.1 + stroke->getControlPoint(cpCount - 3) * 0.9); } std::vector points(cpCount); for (int i = 0; i < cpCount - 1; i++) points[i] = stroke->getControlPoint((cpIndex == 0) ? cpCount - i - 1 : i); points[cpCount - 1] = pos; TStroke *newStroke = new TStroke(points); newStroke->setStyle(styleId); newStroke->outlineOptions() = stroke->outlineOptions(); std::list oldEdgeList, emptyList; computeEdgeList(newStroke, m_strokes[index]->m_edgeList, cpIndex == 0, emptyList, 0, oldEdgeList); std::vector toBeDeleted; toBeDeleted.push_back(index); removeStrokes(toBeDeleted, true, false); insertStrokeAt(new VIStroke(newStroke, groupId), index, false); computeRegions(); transferColors(oldEdgeList, m_strokes[index]->m_edgeList, true, false, true); return m_strokes[index]; } //----------------------------------------------------------------------------- VIStroke *TVectorImage::Imp::extendStroke(int index, const TThickPoint &p, int cpIndex) { TGroupId groupId = m_strokes[index]->m_groupId; TStroke *stroke = m_strokes[index]->m_s; TStroke *ret; int cpCount = stroke->getControlPointCount(); int count = 0; std::vector points(cpCount + 2); int i, incr = (cpIndex == 0) ? -1 : 1; for (i = ((cpIndex == 0) ? cpCount - 1 : 0); i != cpIndex + incr; i += incr) points[count++] = stroke->getControlPoint(i); TThickPoint tp(p, points[count - 1].thick); points[count++] = 0.5 * (stroke->getControlPoint(cpIndex) + tp); points[count++] = tp; TStroke *newStroke = new TStroke(points); newStroke->setStyle(stroke->getStyle()); newStroke->outlineOptions() = stroke->outlineOptions(); ret = newStroke; std::list oldEdgeList, emptyList; if (m_computedAlmostOnce) computeEdgeList(newStroke, m_strokes[index]->m_edgeList, cpIndex == 0, emptyList, false, oldEdgeList); std::vector toBeDeleted; toBeDeleted.push_back(index); removeStrokes(toBeDeleted, true, false); // removeStroke(index, false); insertStrokeAt(new VIStroke(newStroke, groupId), index, false); if (m_computedAlmostOnce) { computeRegions(); transferColors(oldEdgeList, m_strokes[index]->m_edgeList, true, false, true); } return m_strokes[index]; } //----------------------------------------------------------------------------- VIStroke *TVectorImage::Imp::joinStroke(int index1, int index2, int cpIndex1, int cpIndex2) { assert(m_strokes[index1]->m_groupId == m_strokes[index2]->m_groupId); TGroupId groupId = m_strokes[index1]->m_groupId; TStroke *stroke1 = m_strokes[index1]->m_s; TStroke *stroke2 = m_strokes[index2]->m_s; // TStroke* ret; int cpCount1 = stroke1->getControlPointCount(); int cpCount2 = stroke2->getControlPointCount(); int styleId = stroke1->getStyle(); // check if the both ends are at the same postion bool isSamePos = isAlmostZero(tdistance2(stroke1->getControlPoint(cpIndex1), stroke2->getControlPoint(cpIndex2))); // connecting the ends in the same shape at the same postion // means just making the shape self-looped if (isSamePos && index1 == index2) { stroke1->setSelfLoop(); return m_strokes[index1]; } std::vector points; int i, incr = (cpIndex1 == 0) ? -1 : 1; int start = ((cpIndex1 == 0) ? cpCount1 - 1 : 0); int end = (isSamePos) ? cpIndex1 : cpIndex1 + incr; for (i = start; i != end; i += incr) points.push_back(stroke1->getControlPoint(i)); points.push_back(0.5 * (stroke1->getControlPoint(cpIndex1) + stroke2->getControlPoint(cpIndex2))); if (index1 != index2) { incr = (cpIndex2 == 0) ? 1 : -1; start = (isSamePos) ? cpIndex2 + incr : cpIndex2; end = ((cpIndex2 == 0) ? cpCount2 - 1 : 0) + incr; for (i = start; i != end; i += incr) points.push_back(stroke2->getControlPoint(i)); } else points.push_back(stroke2->getControlPoint(cpIndex2)); TStroke *newStroke = new TStroke(points); newStroke->setStyle(styleId); newStroke->outlineOptions() = stroke1->outlineOptions(); // ret = newStroke; if (index1 == index2) newStroke->setSelfLoop(); std::list oldEdgeList, emptyList; computeEdgeList( newStroke, m_strokes[index1]->m_edgeList, cpIndex1 == 0, (index1 != index2) ? m_strokes[index2]->m_edgeList : emptyList, cpIndex2 == 0, oldEdgeList); std::vector toBeDeleted; toBeDeleted.push_back(index1); if (index1 != index2) toBeDeleted.push_back(index2); removeStrokes(toBeDeleted, true, false); insertStrokeAt(new VIStroke(newStroke, groupId), index1, false); computeRegions(); transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); return m_strokes[index1]; } //----------------------------------------------------------------------------- VIStroke *TVectorImage::Imp::joinStrokeSmoothly(int index1, int index2, int cpIndex1, int cpIndex2) { assert(m_strokes[index1]->m_groupId == m_strokes[index2]->m_groupId); TGroupId groupId = m_strokes[index1]->m_groupId; TStroke *stroke1 = m_strokes[index1]->m_s; TStroke *stroke2 = m_strokes[index2]->m_s; TStroke *ret; int cpCount1 = stroke1->getControlPointCount(); int cpCount2 = stroke2->getControlPointCount(); int styleId = stroke1->getStyle(); int qCount1 = stroke1->getChunkCount(); int qCount2 = stroke2->getChunkCount(); const TThickQuadratic *q1 = stroke1->getChunk(cpIndex1 == 0 ? 0 : qCount1 - 1); const TThickQuadratic *q2 = stroke2->getChunk(cpIndex2 == 0 ? 0 : qCount2 - 1); double len1 = q1->getLength(); assert(len1 >= 0); if (len1 <= 0) len1 = 0; double w1 = exp(-len1 * 0.01); double len2 = q2->getLength(); assert(len2 >= 0); if (len2 <= 0) len2 = 0; double w2 = exp(-len2 * 0.01); TThickPoint extreme1 = cpIndex1 == 0 ? q1->getThickP0() : q1->getThickP2(); TThickPoint extreme2 = cpIndex2 == 0 ? q2->getThickP0() : q2->getThickP2(); TThickPoint m1 = q1->getThickP1(); TThickPoint m2 = q2->getThickP1(); TThickPoint p1 = extreme1 * (1 - w1) + m1 * w1; TThickPoint p2 = extreme2 * (1 - w2) + m2 * w2; TThickPoint middleP = (p1 + p2) * 0.5; double angle = fabs(cross(normalize(m1 - middleP), normalize(m2 - middleP))); if (angle < 0.05) middleP = (m1 + m2) * 0.5; stroke1->setControlPoint(cpIndex1, middleP); if (isAlmostZero(len1)) { if (cpIndex1 == 0) stroke1->setControlPoint( 1, middleP * 0.1 + stroke1->getControlPoint(2) * 0.9); else stroke1->setControlPoint( cpCount1 - 2, middleP * 0.1 + stroke1->getControlPoint(cpCount1 - 3) * 0.9); } stroke2->setControlPoint(cpIndex2, middleP); if (isAlmostZero(len2)) { if (cpIndex2 == 0) stroke2->setControlPoint( 1, middleP * 0.1 + stroke2->getControlPoint(2) * 0.9); else stroke2->setControlPoint( cpCount2 - 2, middleP * 0.1 + stroke2->getControlPoint(cpCount2 - 3) * 0.9); } if (stroke1 == stroke2) { std::list oldEdgeList, emptyList; computeEdgeList(stroke1, m_strokes[index1]->m_edgeList, cpIndex1 == 0, emptyList, false, oldEdgeList); eraseIntersection(index1); m_strokes[index1]->m_isNewForFill = true; stroke1->setSelfLoop(); computeRegions(); transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); return m_strokes[index1]; // nundo->m_newStroke=new TStroke(*stroke1); // nundo->m_newStrokeId=stroke1->getId(); } std::vector points; points.reserve(cpCount1 + cpCount2 - 1); int incr = (cpIndex1) ? 1 : -1; int stop = cpIndex1; int i = cpCount1 - 1 - cpIndex1; for (; i != stop; i += incr) points.push_back(stroke1->getControlPoint(i)); incr = (cpIndex2) ? -1 : 1; stop = cpCount2 - 1 - cpIndex2; for (i = cpIndex2; i != stop; i += incr) points.push_back(stroke2->getControlPoint(i)); points.push_back(stroke2->getControlPoint(stop)); TStroke *newStroke = new TStroke(points); newStroke->setStyle(styleId); newStroke->outlineOptions() = stroke1->outlineOptions(); ret = newStroke; // nundo->m_newStroke=new TStroke(*newStroke); // nundo->m_newStrokeId=newStroke->getId(); std::list oldEdgeList; // ofstream os("c:\\temp\\edges.txt"); // printEdges(os, "****edgelist1", getPalette(), // m_imp->m_strokes[index1]->m_edgeList); // printEdges(os, "****edgelist2", getPalette(), // m_imp->m_strokes[index2]->m_edgeList); computeEdgeList(newStroke, m_strokes[index1]->m_edgeList, cpIndex1 == 0, m_strokes[index2]->m_edgeList, cpIndex2 == 0, oldEdgeList); // printEdges(os, "****edgelist", getPalette(), oldEdgeList); std::vector toBeDeleted; toBeDeleted.push_back(index1); toBeDeleted.push_back(index2); removeStrokes(toBeDeleted, true, false); insertStrokeAt(new VIStroke(newStroke, groupId), index1); computeRegions(); transferColors(oldEdgeList, m_strokes[index1]->m_edgeList, true, false, true); return m_strokes[index1]; // TUndoManager::manager()->add(nundo); } //----------------------------------------------------------------------------- VIStroke *TVectorImage::joinStroke(int index1, int index2, int cpIndex1, int cpIndex2, bool isSmooth) { int finalStyle = -1; if (index1 > index2) { finalStyle = getStroke(index1)->getStyle(); std::swap(index1, index2); std::swap(cpIndex1, cpIndex2); } /* if (index1==index2) //selfLoop! { if (index1>0 && index1<(int)getStrokeCount()-1 && !getStroke(index1-1)->isSelfLoop() && !getStroke(index1+1)->isSelfLoop()) { for (UINT i = index1+2; iisSelfLoop(); i++) ; moveStroke(index1, i-1); index1 = index2 = i-1; } } */ VIStroke *ret; if (isSmooth) ret = m_imp->joinStrokeSmoothly(index1, index2, cpIndex1, cpIndex2); else ret = m_imp->joinStroke(index1, index2, cpIndex1, cpIndex2); if (finalStyle != -1) getStroke(index1)->setStyle(finalStyle); return ret; } //----------------------------------------------------------------------------- VIStroke *TVectorImage::extendStroke(int index, const TThickPoint &p, int cpIndex, bool isSmooth) { if (isSmooth) return m_imp->extendStrokeSmoothly(index, p, cpIndex); else return m_imp->extendStroke(index, p, cpIndex); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- TInputStreamInterface &TInputStreamInterface::operator>>(TPixel32 &pixel) { return *this >> pixel.r >> pixel.g >> pixel.b >> pixel.m; } //------------------------------------------------------------------- TOutputStreamInterface &TOutputStreamInterface::operator<<( const TPixel32 &pixel) { return *this << pixel.r << pixel.g << pixel.b << pixel.m; } //------------------------------------------------------------------- void TVectorImage::setAutocloseTolerance(double val) { m_imp->m_autocloseTolerance = val; } //------------------------------------------------------------------- double TVectorImage::getAutocloseTolerance() const { return m_imp->m_autocloseTolerance; } //------------------------------------------------------------------- TThread::Mutex *TVectorImage::getMutex() const { return m_imp->m_mutex; } //------------------------------------------------------------------- void TVectorImage::areaFill(TStroke *stroke, int index, bool m_onlyUnfilled) { TVectorImage v; v.addStroke(stroke); v.findRegions(); for (UINT i = 0; i < v.getRegionCount(); i++) for (UINT j = 0; j < getRegionCount(); j++) { if (m_imp->m_insideGroup != TGroupId() && !m_imp->m_insideGroup.isParentOf( m_imp->m_strokes[getRegion(j)->getEdge(0)->m_index]->m_groupId)) continue; if (v.getRegion(i)->contains(*getRegion(j))) getRegion(j)->setStyle(index); } v.removeStroke(0); } VIStroke *cloneVIStroke(VIStroke *vs) { return new VIStroke(*vs); } void deleteVIStroke(VIStroke *vs) { delete vs; vs = 0; } //------------------------------------------------------------------- bool TVectorImage::sameSubGroup(int index0, int index1) const { if (index0 < 0 || index1 < 0) return 0; return m_imp->m_strokes[index0]->m_groupId.getCommonParentDepth( m_imp->m_strokes[index1]->m_groupId) > m_imp->m_insideGroup.getDepth(); } //------------------------------------------------------------------- int TVectorImage::getCommonGroupDepth(int index0, int index1) const { if (index0 < 0 || index1 < 0) return 0; return m_imp->m_strokes[index0]->m_groupId.getCommonParentDepth( m_imp->m_strokes[index1]->m_groupId); } //------------------------------------------------------------------- int TVectorImage::ungroup(int fromIndex) { m_imp->m_insideGroup = TGroupId(); assert(m_imp->m_strokes[fromIndex]->m_groupId.isGrouped() != 0); std::vector changedStrokes; int toIndex = fromIndex + 1; while (toIndex < (int)m_imp->m_strokes.size() && m_imp->m_strokes[fromIndex]->m_groupId.getCommonParentDepth( m_imp->m_strokes[toIndex]->m_groupId) >= 1) toIndex++; toIndex--; TGroupId groupId; if (fromIndex > 0 && m_imp->m_strokes[fromIndex - 1]->m_groupId.isGrouped(true) != 0) groupId = m_imp->m_strokes[fromIndex - 1]->m_groupId; else if (toIndex < (int)m_imp->m_strokes.size() - 1 && m_imp->m_strokes[toIndex + 1]->m_groupId.isGrouped(true) != 0) groupId = m_imp->m_strokes[toIndex + 1]->m_groupId; else groupId = TGroupId(this, true); for (int i = fromIndex; i <= toIndex || (i < (int)m_imp->m_strokes.size() && m_imp->m_strokes[i]->m_groupId.isGrouped(true) != 0); i++) { m_imp->m_strokes[i]->m_groupId.ungroup(groupId); changedStrokes.push_back(i); } notifyChangedStrokes(changedStrokes, std::vector(), false); return toIndex - fromIndex + 1; } //------------------------------------------------------------------- bool TVectorImage::isEnteredGroupStroke(int index) const { return m_imp->m_insideGroup.isParentOf(getVIStroke(index)->m_groupId); } //------------------------------------------------------------------- bool TVectorImage::enterGroup(int index) { VIStroke *vs = getVIStroke(index); if (!vs->m_groupId.isGrouped()) return false; int newDepth = vs->m_groupId.getCommonParentDepth(m_imp->m_insideGroup) + 1; TGroupId newGroupId = vs->m_groupId; while (newGroupId.getDepth() > newDepth) newGroupId = newGroupId.getParent(); if (newGroupId == m_imp->m_insideGroup) return false; m_imp->m_insideGroup = newGroupId; return true; } //------------------------------------------------------------------- int TVectorImage::exitGroup() { if (m_imp->m_insideGroup == TGroupId()) return -1; int i, ret = -1; for (i = 0; i < (int)m_imp->m_strokes.size(); i++) { if (m_imp->m_strokes[i]->m_groupId.getCommonParentDepth( m_imp->m_insideGroup) >= m_imp->m_insideGroup.getDepth()) { ret = i; break; } } assert(i != m_imp->m_strokes.size()); m_imp->m_insideGroup = m_imp->m_insideGroup.getParent(); return ret; } //------------------------------------------------------------------- void TVectorImage::group(int fromIndex, int count) { int i; assert(count >= 0); std::vector changedStroke; TGroupId parent = TGroupId(this, false); for (i = 0; i < count; i++) { m_imp->m_strokes[fromIndex + i]->m_groupId = TGroupId(parent, m_imp->m_strokes[fromIndex + i]->m_groupId); changedStroke.push_back(fromIndex + i); } m_imp->rearrangeMultiGroup(); // see method's comment m_imp->regroupGhosts(changedStroke); notifyChangedStrokes(changedStroke, std::vector(), false); #ifdef _DEBUG m_imp->checkGroups(); #endif } //------------------------------------------------------------------- int TVectorImage::getGroupDepth(UINT index) const { assert(index < m_imp->m_strokes.size()); return m_imp->m_strokes[index]->m_groupId.isGrouped(); } //------------------------------------------------------------------- int TVectorImage::areDifferentGroup(UINT index1, bool isRegion1, UINT index2, bool isRegion2) const { return m_imp->areDifferentGroup(index1, isRegion1, index2, isRegion2); } //------------------------------------------------------------------- /*this method is tricky. it is not allow to have not-adiacent strokes of same group. but it can happen when you group some already-grouped strokes creating sub-groups. example: vi made of 5 strokes, before grouping (N=no group) N N 1 1 N after grouping became: 2 2 2-1 2-1 2 not allowed! this method moves strokes, so that adiacent strokes have same group. so after calling rearrangeMultiGroup the vi became: 2 2 2 2-1 2-1 */ void TVectorImage::Imp::rearrangeMultiGroup() { UINT i, j, k; if (m_strokes.size() <= 0) return; for (i = 0; i < m_strokes.size() - 1; i++) { if (m_strokes[i]->m_groupId.isGrouped() && m_strokes[i + 1]->m_groupId.isGrouped() && m_strokes[i]->m_groupId != m_strokes[i + 1]->m_groupId) { TGroupId &prevId = m_strokes[i]->m_groupId; TGroupId &idToMove = m_strokes[i + 1]->m_groupId; for (j = i + 1; j < m_strokes.size() && m_strokes[j]->m_groupId == idToMove; j++) ; if (j != m_strokes.size()) { j--; // now range i+1-j contains the strokes to be moved. // let's compute where to move them (after last for (k = j; k < m_strokes.size() && m_strokes[k]->m_groupId != prevId; k++) ; if (k < m_strokes.size()) { for (; k < m_strokes.size() && m_strokes[k]->m_groupId == prevId; k++) ; moveStrokes(i + 1, j - i, k, false); rearrangeMultiGroup(); return; } } } } } //------------------------------------------------------------------- int TVectorImage::Imp::areDifferentGroup(UINT index1, bool isRegion1, UINT index2, bool isRegion2) const { TGroupId group1, group2; if (isRegion1) { TRegion *r = m_regions[index1]; for (UINT i = 0; i < r->getEdgeCount(); i++) if (r->getEdge(i)->m_index >= 0) { group1 = m_strokes[r->getEdge(i)->m_index]->m_groupId; break; } } else group1 = m_strokes[index1]->m_groupId; if (isRegion2) { TRegion *r = m_regions[index2]; for (UINT i = 0; i < r->getEdgeCount(); i++) if (r->getEdge(i)->m_index >= 0) { group2 = m_strokes[r->getEdge(i)->m_index]->m_groupId; break; } } else group2 = m_strokes[index2]->m_groupId; if (!group1 && !group2) return 0; if (group1 == group2) return -1; else return group1.getCommonParentDepth(group2); } //------------------------------------------------------------------- int TGroupId::getCommonParentDepth(const TGroupId &id) const { int size1 = m_id.size(); int size2 = id.m_id.size(); int count; for (count = 0; count < std::min(size1, size2); count++) if (m_id[size1 - count - 1] != id.m_id[size2 - count - 1]) break; return count; } //------------------------------------------------------------------- TGroupId::TGroupId(const TGroupId &parent, const TGroupId &id) { assert(parent.m_id[0] > 0); assert(id.m_id.size() > 0); if (id.isGrouped(true) != 0) m_id.push_back(parent.m_id[0]); else { m_id = id.m_id; int i; for (i = 0; i < (int)parent.m_id.size(); i++) m_id.push_back(parent.m_id[i]); } } /* bool TGroupId::sameParent(const TGroupId& id) const { assert(!m_id.empty() || !id.m_id.empty()); return m_id.back()==id.m_id.back(); } */ TGroupId TGroupId::getParent() const { if (m_id.size() <= 1) return TGroupId(); TGroupId ret = *this; ret.m_id.erase(ret.m_id.begin()); return ret; } void TGroupId::ungroup(const TGroupId &id) { assert(id.isGrouped(true) != 0); assert(!m_id.empty()); if (m_id.size() == 1) m_id[0] = id.m_id[0]; else m_id.pop_back(); } bool TGroupId::operator==(const TGroupId &id) const { if (m_id.size() != id.m_id.size()) return false; UINT i; for (i = 0; i < m_id.size(); i++) if (m_id[i] != id.m_id[i]) return false; return true; } bool TGroupId::operator<(const TGroupId &id) const { assert(!m_id.empty() && !id.m_id.empty()); int size1 = m_id.size(); int size2 = id.m_id.size(); int i; for (i = 0; i < std::min(size1, size2); i++) if (m_id[size1 - i - 1] != id.m_id[size2 - i - 1]) return m_id[size1 - i - 1] < id.m_id[size2 - i - 1]; return size1 < size2; } //------------------------------------------------------------------- int TGroupId::isGrouped(bool implicit) const { assert(!m_id.empty()); assert(m_id[0] != 0); if (implicit) return (m_id[0] < 0) ? 1 : 0; else return (m_id[0] > 0) ? m_id.size() : 0; } //------------------------------------------------------------------- TGroupId::TGroupId(TVectorImage *vi, bool isGhost) { m_id.push_back((isGhost) ? -(++vi->m_imp->m_maxGhostGroupId) : ++vi->m_imp->m_maxGroupId); } #ifdef _DEBUG void TVectorImage::Imp::checkGroups() { TGroupId currGroupId; std::set groupSet; std::set::iterator it; UINT i = 0; while (i < m_strokes.size()) { // assert(m_strokes[i]->m_groupId!=currGroupId); // assert(i==0 || // m_strokes[i-1]->m_groupId.isGrouped()!=m_strokes[i]->m_groupId.isGrouped()!=0 // || // (m_strokes[i]->m_groupId.isGrouped()!=0 && // m_strokes[i-1]->m_groupId!=m_strokes[i]->m_groupId)); currGroupId = m_strokes[i]->m_groupId; it = groupSet.find(currGroupId); if (it != groupSet.end()) // esisteva gia un gruppo con questo id! assert(!"errore: due gruppi con lo stesso id!"); else groupSet.insert(currGroupId); while (i < m_strokes.size() && m_strokes[i]->m_groupId == currGroupId) i++; } } #endif //------------------------------------------------------------------- bool TVectorImage::canMoveStrokes(int strokeIndex, int count, int moveBefore) const { return m_imp->canMoveStrokes(strokeIndex, count, moveBefore); } //------------------------------------------------------------------- // verifica se si possono spostare le stroke da strokeindex a // strokeindex+count-1 prima della posizione moveBefore; // per fare questo fa un vettore in cui mette tutti i gruppi nella posizione // dopo lo // spostamento e verifica che sia un configurazione di gruppi ammessa. bool TVectorImage::Imp::canMoveStrokes(int strokeIndex, int count, int moveBefore) const { if (m_maxGroupId <= 1) // non ci sono gruppi! return true; int i, j = 0; std::vector groupsAfterMoving(m_strokes.size()); if (strokeIndex < moveBefore) { for (i = 0; i < strokeIndex; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = strokeIndex + count; i < moveBefore; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = strokeIndex; i < strokeIndex + count; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = moveBefore; i < (int)m_strokes.size(); i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; } else { for (i = 0; i < moveBefore; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = strokeIndex; i < strokeIndex + count; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = moveBefore; i < strokeIndex; i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; for (i = strokeIndex + count; i < (int)m_strokes.size(); i++) groupsAfterMoving[j++] = m_strokes[i]->m_groupId; } assert(j == (int)m_strokes.size()); i = 0; TGroupId currGroupId; std::set groupSet; while (i < (int)groupsAfterMoving.size()) { currGroupId = groupsAfterMoving[i]; if (groupSet.find(currGroupId) != groupSet.end()) // esisteva gia un gruppo con questo id! { if (!currGroupId.isGrouped(true)) // i gruppi impliciti non contano return false; } else groupSet.insert(currGroupId); while (i < (int)groupsAfterMoving.size() && groupsAfterMoving[i] == currGroupId) i++; } return true; } //----------------------------------------------------------------- void TVectorImage::Imp::regroupGhosts(std::vector &changedStrokes) { TGroupId currGroupId; std::set groupMap; std::set::iterator it; UINT i = 0; while (i < m_strokes.size()) { assert(m_strokes[i]->m_groupId != currGroupId); assert(i == 0 || m_strokes[i - 1]->m_groupId.isGrouped() != m_strokes[i]->m_groupId.isGrouped() != 0 || (m_strokes[i]->m_groupId.isGrouped() != 0 && m_strokes[i - 1]->m_groupId != m_strokes[i]->m_groupId)); currGroupId = m_strokes[i]->m_groupId; it = groupMap.find(currGroupId); if (it != groupMap.end()) // esisteva gia un gruppo con questo id! { if (currGroupId.isGrouped() != 0) assert(!"errore: due gruppi con lo stesso id!"); else // gruppo ghost; gli do un nuovo id { TGroupId newGroup(m_vi, true); while (i < m_strokes.size() && m_strokes[i]->m_groupId.isGrouped(true) != 0) { m_strokes[i]->m_groupId = newGroup; changedStrokes.push_back(i); i++; } } } else { groupMap.insert(currGroupId); while (i < m_strokes.size() && ((currGroupId.isGrouped(false) != 0 && m_strokes[i]->m_groupId == currGroupId) || (currGroupId.isGrouped(true) != 0 && m_strokes[i]->m_groupId.isGrouped(true) != 0))) { if (m_strokes[i]->m_groupId != currGroupId) { m_strokes[i]->m_groupId = currGroupId; changedStrokes.push_back(i); } i++; } } } } //-------------------------------------------------------------- bool TVectorImage::canEnterGroup(int strokeIndex) const { VIStroke *vs = m_imp->m_strokes[strokeIndex]; if (!vs->m_groupId.isGrouped()) return false; return m_imp->m_insideGroup == TGroupId() || vs->m_groupId != m_imp->m_insideGroup; } //-------------------------------------------------------------- bool TVectorImage::inCurrentGroup(int strokeIndex) const { return m_imp->inCurrentGroup(strokeIndex); } //---------------------------------------------------------------------------------- bool TVectorImage::Imp::inCurrentGroup(int strokeIndex) const { return m_insideGroup == TGroupId() || m_insideGroup.isParentOf(m_strokes[strokeIndex]->m_groupId); } //-------------------------------------------------------------------------------------------------- bool TVectorImage::selectable(int strokeIndex) const { return (m_imp->m_insideGroup != m_imp->m_strokes[strokeIndex]->m_groupId && inCurrentGroup(strokeIndex)); } //-------------------------------------------------------------------------------------------------- namespace { bool containsNoSubregion(const TRegion *r, const TPointD &p) { if (r->contains(p)) { for (unsigned int i = 0; i < r->getSubregionCount(); i++) if (r->getSubregion(i)->contains(p)) return false; return true; } else return false; } }; // namespace //------------------------------------------------------ int TVectorImage::getGroupByStroke(UINT index) const { VIStroke *viStroke = getVIStroke(index); return viStroke->m_groupId.m_id.back(); } //------------------------------------------------------ int TVectorImage::getGroupByRegion(UINT index) const { TRegion *r = m_imp->m_regions[index]; for (UINT i = 0; i < r->getEdgeCount(); i++) if (r->getEdge(i)->m_index >= 0) { return m_imp->m_strokes[r->getEdge(i)->m_index]->m_groupId.m_id.back(); } return -1; } //------------------------------------------------------ int TVectorImage::pickGroup(const TPointD &pos, bool onEnteredGroup) const { if (onEnteredGroup && isInsideGroup() == 0) return -1; // double maxDist2 = 50*tglGetPixelSize2(); int strokeIndex = getStrokeCount() - 1; while (strokeIndex >= 0) // ogni ciclo di while esplora un gruppo; ciclo sugli stroke { if (!isStrokeGrouped(strokeIndex)) { strokeIndex--; continue; } bool entered = isInsideGroup() > 0 && isEnteredGroupStroke(strokeIndex); if ((onEnteredGroup || entered) && (!onEnteredGroup || !entered)) { strokeIndex--; continue; } int currStrokeIndex = strokeIndex; while (strokeIndex >= 0 && getCommonGroupDepth(strokeIndex, currStrokeIndex) > 0) { TStroke *s = getStroke(strokeIndex); double outT; int chunkIndex; double dist2; bool ret = s->getNearestChunk(pos, outT, chunkIndex, dist2); if (ret) { TThickPoint p = s->getChunk(chunkIndex)->getThickPoint(outT); if (p.thick < 0.1) p.thick = 1; if (sqrt(dist2) <= 1.5 * p.thick) return strokeIndex; } /*TThickPoint p = s->getThickPoint(s->getW(pos)); double dist = tdistance( TThickPoint(pos,0), p); if (dist<1.2*p.thick/2.0) return strokeIndex;*/ strokeIndex--; } } strokeIndex = getStrokeCount() - 1; int ret = -1; while (strokeIndex >= 0) // ogni ciclo di while esplora un gruppo; ciclo sulle regions { if (!isStrokeGrouped(strokeIndex)) { strokeIndex--; continue; } bool entered = isInsideGroup() > 0 && isEnteredGroupStroke(strokeIndex); if ((onEnteredGroup || entered) && (!onEnteredGroup || !entered)) { strokeIndex--; continue; } TRegion *currR = 0; for (UINT regionIndex = 0; regionIndex < getRegionCount(); regionIndex++) { TRegion *r = getRegion(regionIndex); int i, regionStrokeIndex = -1; for (i = 0; i < (int)r->getEdgeCount() && regionStrokeIndex < 0; i++) regionStrokeIndex = r->getEdge(i)->m_index; if (regionStrokeIndex >= 0 && sameSubGroup(regionStrokeIndex, strokeIndex) && containsNoSubregion(r, pos)) { if (!currR || currR->contains(*r)) { currR = r; ret = regionStrokeIndex; } } } if (currR != 0) { assert(m_palette); const TSolidColorStyle *st = dynamic_cast( m_palette->getStyle(currR->getStyle())); if (!st || st->getMainColor() != TPixel::Transparent) return ret; } while (strokeIndex > 0 && getCommonGroupDepth(strokeIndex, strokeIndex - 1) > 0) strokeIndex--; strokeIndex--; } return -1; } //------------------------------------------------------------------------------------ int TVectorImage::pickGroup(const TPointD &pos) const { int index; if ((index = pickGroup(pos, true)) == -1) return pickGroup(pos, false); return index; } //--------------------------------------------------------------------------------------------------