| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TNZ_LITTLE_ENDIAN undefined !! |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| using namespace std; |
| |
| typedef TVectorImage::IntersectionBranch IntersectionBranch; |
| |
| |
| inline double myRound(double x) { |
| return (1.0 / REGION_COMPUTING_PRECISION) * |
| ((long)(x * REGION_COMPUTING_PRECISION)); |
| } |
| |
| inline TThickPoint myRound(const TThickPoint &p) { |
| return TThickPoint(myRound(p.x), myRound(p.y), p.thick); |
| } |
| |
| void print(list<Intersection> &intersectionList, char *str) { |
| ofstream of(str); |
| |
| of << "***************************" << endl; |
| |
| list<Intersection>::const_iterator it; |
| list<IntersectedStroke>::const_iterator it1; |
| int i, j; |
| for (i = 0, it = intersectionList.begin(); it != intersectionList.end(); |
| it++, i++) { |
| of << "***************************" << endl; |
| of << "Intersection#" << i << ": " << it->m_intersection |
| << "numBranches: " << it->m_numInter << endl; |
| of << endl; |
| |
| for (j = 0, it1 = it->m_strokeList.begin(); it1 != it->m_strokeList.end(); |
| it1++, j++) { |
| of << "----Branch #" << j; |
| if (it1->m_edge.m_index < 0) of << "(AUTOCLOSE)"; |
| of << "Intersection at " << it1->m_edge.m_w0 << ": " |
| << ": " << endl; |
| of << "ColorId: " << it1->m_edge.m_styleId << endl; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| of << "----Stroke " << (it1->m_gettingOut ? "OUT" : "IN") << " #" |
| << it1->m_edge.m_index << ": " << endl; |
| |
| |
| |
| { |
| of << "---- NEXT Intersection:"; |
| if (it1->m_nextIntersection != intersectionList.end()) { |
| int dist = |
| std::distance(intersectionList.begin(), it1->m_nextIntersection); |
| of << dist; |
| list<Intersection>::iterator iit = intersectionList.begin(); |
| std::advance(iit, dist); |
| of << " " |
| << std::distance(iit->m_strokeList.begin(), it1->m_nextStroke); |
| } |
| |
| else |
| of << "NULL!!"; |
| of << "---- NEXT Stroke:"; |
| if (it1->m_nextIntersection != intersectionList.end()) |
| of << it1->m_nextStroke->m_edge.m_index; |
| else |
| of << "NULL!!"; |
| } |
| of << endl << endl; |
| } |
| } |
| } |
| |
| void findNearestIntersection(list<Intersection> &interList, |
| const list<Intersection>::iterator &i1, |
| const list<IntersectedStroke>::iterator &i2); |
| |
| |
| |
| |
| void checkInterList(list<Intersection> &intersectionList) { |
| list<Intersection>::iterator it; |
| list<IntersectedStroke>::iterator it1; |
| |
| for (it = intersectionList.begin(); it != intersectionList.end(); it++) { |
| int count = 0; |
| for (it1 = it->m_strokeList.begin(); it1 != it->m_strokeList.end(); it1++) { |
| int val; |
| if (it1->m_nextIntersection != intersectionList.end()) { |
| count++; |
| |
| assert(it1->m_nextStroke->m_nextIntersection == it); |
| assert(it1->m_nextStroke->m_nextStroke == it1); |
| |
| |
| val = std::distance(intersectionList.begin(), it1->m_nextIntersection); |
| } |
| |
| |
| } |
| assert(count == it->m_numInter); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void addIntersections(IntersectionData &intersectionData, |
| const vector<VIStroke *> &s, int ii, int jj, |
| const vector<DoublePair> &intersections, int numStrokes); |
| |
| void addIntersection(IntersectionData &intData, const vector<VIStroke *> &s, |
| int ii, int jj, DoublePair intersections, int strokeSize); |
| |
| |
| |
| bool sortBBox(const TStroke *s1, const TStroke *s2) { |
| return s1->getBBox().x0 < s2->getBBox().x0; |
| } |
| |
| |
| |
| void cleanIntersectionMarks(list<Intersection> &interList) { |
| for (list<Intersection>::iterator it1 = interList.begin(); |
| it1 != interList.end(); it1++) |
| for (list<IntersectedStroke>::iterator it2 = (*it1).m_strokeList.begin(); |
| it2 != (*it1).m_strokeList.end(); it2++) { |
| it2->m_visited = |
| false; |
| |
| |
| if (it2->m_nextIntersection != interList.end()) { |
| it2->m_nextIntersection = |
| interList.end(); |
| it1->m_numInter--; |
| } |
| } |
| } |
| |
| |
| |
| void cleanNextIntersection(list<Intersection> &interList, TStroke *s) { |
| for (list<Intersection>::iterator it1 = interList.begin(); |
| it1 != interList.end(); it1++) |
| for (list<IntersectedStroke>::iterator it2 = (*it1).m_strokeList.begin(); |
| it2 != (*it1).m_strokeList.end(); it2++) |
| if (it2->m_edge.m_s == s) { |
| |
| |
| if (it2->m_nextIntersection != interList.end()) { |
| it2->m_nextIntersection = interList.end(); |
| it1->m_numInter--; |
| } |
| it2->m_nextStroke = (*it1).m_strokeList.end(); |
| } |
| } |
| |
| |
| |
| void TVectorImage::Imp::eraseEdgeFromStroke( |
| list<IntersectedStroke>::iterator it2) { |
| if (it2->m_edge.m_index >= |
| 0) |
| { |
| VIStroke *s; |
| s = m_strokes[it2->m_edge.m_index]; |
| assert(s->m_s == it2->m_edge.m_s); |
| list<TEdge *>::iterator iit = s->m_edgeList.begin(), |
| it_e = s->m_edgeList.end(); |
| |
| for (; iit != it_e; ++iit) |
| if ((*iit)->m_w0 == it2->m_edge.m_w0 && |
| (*iit)->m_w1 == it2->m_edge.m_w1) { |
| assert((*iit)->m_toBeDeleted == false); |
| s->m_edgeList.erase(iit); |
| return; |
| } |
| } |
| } |
| |
| |
| |
| list<IntersectedStroke>::iterator TVectorImage::Imp::eraseBranch( |
| list<Intersection>::iterator it1, list<IntersectedStroke>::iterator it2) { |
| |
| |
| list<Intersection> &intList = m_intersectionData.m_intList; |
| |
| if (it2->m_nextIntersection != intList.end()) { |
| list<Intersection>::iterator nextInt = it2->m_nextIntersection; |
| list<IntersectedStroke>::iterator nextStroke = it2->m_nextStroke; |
| assert(nextStroke->m_nextIntersection == it1); |
| assert(nextStroke->m_nextStroke == it2); |
| assert(nextStroke != it2); |
| |
| |
| |
| |
| if (nextStroke->m_nextIntersection != intList.end()) { |
| nextStroke->m_nextIntersection = intList.end(); |
| nextInt->m_numInter--; |
| } |
| |
| |
| } |
| if (it2->m_nextIntersection != intList.end()) it1->m_numInter--; |
| |
| eraseEdgeFromStroke(it2); |
| |
| it2->m_edge.m_w0 = it2->m_edge.m_w1 = -3; |
| it2->m_edge.m_index = -3; |
| it2->m_edge.m_s = 0; |
| it2->m_edge.m_styleId = -3; |
| |
| list<IntersectedStroke>::iterator ret = (*it1).m_strokeList.erase(it2); |
| |
| return ret; |
| } |
| |
| |
| |
| void TVectorImage::Imp::eraseDeadIntersections() { |
| list<Intersection>::iterator it; |
| |
| for (it = m_intersectionData.m_intList.begin(); |
| it != m_intersectionData.m_intList.end();) |
| |
| |
| { |
| list<Intersection> &intList = m_intersectionData.m_intList; |
| |
| if (it->m_strokeList.size() == 1) { |
| eraseBranch(it, (*it).m_strokeList.begin()); |
| assert(it->m_strokeList.empty()); |
| it = intList.erase(it); |
| } else if (it->m_strokeList.size() == 2 && |
| ((*it).m_strokeList.front().m_edge.m_s == |
| (*it).m_strokeList.back().m_edge.m_s && |
| (*it).m_strokeList.front().m_edge.m_w0 == |
| (*it).m_strokeList.back().m_edge.m_w0)) |
| |
| { |
| list<IntersectedStroke>::iterator it1 = it->m_strokeList.begin(), iit1, |
| iit2; |
| list<IntersectedStroke>::iterator it2 = it1; |
| it2++; |
| |
| eraseEdgeFromStroke(it1); |
| eraseEdgeFromStroke(it2); |
| |
| iit1 = (it1->m_nextIntersection == intList.end()) ? NULL_ITER |
| : it1->m_nextStroke; |
| iit2 = (it2->m_nextIntersection == intList.end()) ? NULL_ITER |
| : it2->m_nextStroke; |
| |
| if (iit1 != NULL_ITER && iit2 != NULL_ITER) { |
| iit1->m_edge.m_w1 = iit2->m_edge.m_w0; |
| iit2->m_edge.m_w1 = iit1->m_edge.m_w0; |
| } |
| if (iit1 != NULL_ITER) { |
| iit1->m_nextStroke = iit2; |
| iit1->m_nextIntersection = it2->m_nextIntersection; |
| if (iit1->m_nextIntersection == intList.end()) |
| it1->m_nextIntersection->m_numInter--; |
| } |
| |
| if (iit2 != NULL_ITER) { |
| iit2->m_nextStroke = iit1; |
| iit2->m_nextIntersection = it1->m_nextIntersection; |
| if (iit2->m_nextIntersection == intList.end()) |
| it2->m_nextIntersection->m_numInter--; |
| } |
| |
| it->m_strokeList.clear(); |
| it->m_numInter = 0; |
| it = intList.erase(it); |
| } else |
| ++it; |
| } |
| } |
| |
| |
| |
| void TVectorImage::Imp::doEraseIntersection(int index, |
| vector<int> *toBeDeleted) { |
| list<Intersection> &interList = m_intersectionData.m_intList; |
| |
| list<Intersection>::iterator it1 = interList.begin(); |
| TStroke *deleteIt = 0; |
| |
| while (it1 != interList.end()) { |
| bool removeAutocloses = false; |
| |
| list<IntersectedStroke>::iterator it2 = (*it1).m_strokeList.begin(); |
| |
| while (it2 != (*it1).m_strokeList.end()) { |
| IntersectedStroke &is = *it2; |
| |
| if (is.m_edge.m_index == index) { |
| if (is.m_edge.m_index >= 0) |
| |
| removeAutocloses = true; |
| else |
| deleteIt = is.m_edge.m_s; |
| it2 = eraseBranch(it1, it2); |
| } else |
| ++it2; |
| |
| } |
| if (removeAutocloses) |
| |
| { |
| assert(toBeDeleted); |
| for (it2 = (*it1).m_strokeList.begin(); it2 != (*it1).m_strokeList.end(); |
| it2++) |
| if (it2->m_edge.m_index < 0 && |
| (it2->m_edge.m_w0 == 1 || it2->m_edge.m_w0 == 0)) |
| toBeDeleted->push_back(it2->m_edge.m_index); |
| } |
| |
| if ((*it1).m_strokeList.empty()) |
| it1 = interList.erase(it1); |
| else |
| it1++; |
| } |
| |
| if (deleteIt) delete deleteIt; |
| } |
| |
| |
| |
| UINT TVectorImage::Imp::getFillData(IntersectionBranch *&v) { |
| |
| |
| list<Intersection> &intList = m_intersectionData.m_intList; |
| if (intList.empty()) return 0; |
| |
| list<Intersection>::iterator it1; |
| list<IntersectedStroke>::iterator it2; |
| UINT currInt = 0; |
| vector<UINT> branchesBefore(intList.size() + 1); |
| |
| branchesBefore[0] = 0; |
| UINT count = 0, size = 0; |
| |
| for (it1 = intList.begin(); it1 != intList.end(); ++it1, currInt++) { |
| UINT strokeListSize = it1->m_strokeList.size(); |
| size += strokeListSize; |
| branchesBefore[currInt + 1] = branchesBefore[currInt] + strokeListSize; |
| } |
| |
| v = new IntersectionBranch[size]; |
| currInt = 0; |
| |
| for (it1 = intList.begin(); it1 != intList.end(); ++it1, currInt++) { |
| UINT currBranch = 0; |
| for (it2 = it1->m_strokeList.begin(); it2 != it1->m_strokeList.end(); |
| ++it2, currBranch++) { |
| IntersectionBranch &b = v[count]; |
| b.m_currInter = currInt; |
| b.m_strokeIndex = it2->m_edge.m_index; |
| b.m_w = it2->m_edge.m_w0; |
| b.m_style = it2->m_edge.m_styleId; |
| |
| b.m_gettingOut = it2->m_gettingOut; |
| if (it2->m_nextIntersection == intList.end()) |
| b.m_nextBranch = count; |
| else { |
| UINT distInt = std::distance(intList.begin(), it2->m_nextIntersection); |
| UINT distBranch = std::distance( |
| it2->m_nextIntersection->m_strokeList.begin(), it2->m_nextStroke); |
| |
| if ((distInt < currInt) || |
| (distInt == currInt && distBranch < currBranch)) { |
| UINT posNext = branchesBefore[distInt] + distBranch; |
| assert(posNext < count); |
| b.m_nextBranch = posNext; |
| assert(v[posNext].m_nextBranch == (std::numeric_limits<UINT>::max)()); |
| v[posNext].m_nextBranch = count; |
| } else |
| b.m_nextBranch = (std::numeric_limits<UINT>::max)(); |
| } |
| count++; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return size; |
| } |
| |
| |
| |
| |
| namespace { |
| TStroke *reconstructAutocloseStroke(list<Intersection> &intList, |
| list<Intersection>::iterator it1, |
| list<IntersectedStroke>::iterator it2) |
| |
| { |
| bool found = false; |
| list<Intersection>::iterator iit1 = it1; |
| list<IntersectedStroke>::iterator iit2; |
| iit1++; |
| |
| for (; !found && iit1 != intList.end(); iit1++) { |
| for (iit2 = iit1->m_strokeList.begin(); |
| !found && iit2 != iit1->m_strokeList.end(); iit2++) { |
| if (it2->m_edge.m_index == iit2->m_edge.m_index) { |
| if ((iit2->m_edge.m_w0 == 1 && it2->m_edge.m_w0 == 0) || |
| (iit2->m_edge.m_w0 == 0 && it2->m_edge.m_w0 == 1)) { |
| found = true; |
| vector<TPointD> v; |
| if (it2->m_edge.m_w0 == 0) { |
| v.push_back(it1->m_intersection); |
| v.push_back(iit1->m_intersection); |
| } else { |
| v.push_back(iit1->m_intersection); |
| v.push_back(it1->m_intersection); |
| } |
| it2->m_edge.m_s = iit2->m_edge.m_s = new TStroke(v); |
| |
| |
| } |
| |
| |
| } |
| } |
| } |
| assert(found); |
| if (!found) it2->m_edge.m_s = 0; |
| |
| return it2->m_edge.m_s; |
| } |
| |
| } |
| |
| |
| void TVectorImage::Imp::setFillData(IntersectionBranch *v, UINT branchCount) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (branchCount == 0) return; |
| |
| list<Intersection> &intList = m_intersectionData.m_intList; |
| clearPointerContainer(m_regions); |
| m_regions.clear(); |
| intList.clear(); |
| list<Intersection>::iterator currInt; |
| list<IntersectedStroke>::iterator currBranch; |
| |
| vector<UINT> branchesBefore(v[branchCount - 1].m_currInter + 1); |
| |
| UINT i = 0; |
| for (; i < branchCount; i++) { |
| const IntersectionBranch &b = v[i]; |
| if (i == 0 || v[i].m_currInter != v[i - 1].m_currInter) { |
| assert(i == 0 || v[i].m_currInter == v[i - 1].m_currInter + 1); |
| |
| branchesBefore[v[i].m_currInter] = i; |
| intList.push_back(Intersection()); |
| currInt = intList.begin(); |
| advance(currInt, intList.size() - 1); |
| } |
| |
| IntersectedStroke is; |
| currInt->m_strokeList.push_back(is); |
| currBranch = currInt->m_strokeList.begin(); |
| advance(currBranch, currInt->m_strokeList.size() - 1); |
| |
| currBranch->m_edge.m_styleId = b.m_style; |
| |
| currBranch->m_edge.m_index = b.m_strokeIndex; |
| if (b.m_strokeIndex >= 0) |
| currBranch->m_edge.m_s = m_strokes[b.m_strokeIndex]->m_s; |
| else |
| currBranch->m_edge.m_s = 0; |
| currBranch->m_gettingOut = b.m_gettingOut; |
| currBranch->m_edge.m_w0 = b.m_w; |
| currBranch->m_edge.m_w1 = v[b.m_nextBranch].m_w; |
| assert(currBranch->m_edge.m_w0 >= -1e-8 && |
| currBranch->m_edge.m_w0 <= 1 + 1e-8); |
| assert(currBranch->m_edge.m_w1 >= -1e-8 && |
| currBranch->m_edge.m_w1 <= 1 + 1e-8); |
| |
| if (b.m_nextBranch < i) { |
| list<Intersection>::iterator it1; |
| list<IntersectedStroke>::iterator it2; |
| it1 = intList.begin(); |
| std::advance(it1, v[b.m_nextBranch].m_currInter); |
| it2 = it1->m_strokeList.begin(); |
| assert(b.m_nextBranch - branchesBefore[v[b.m_nextBranch].m_currInter] >= |
| 0); |
| |
| std::advance( |
| it2, b.m_nextBranch - branchesBefore[v[b.m_nextBranch].m_currInter]); |
| |
| currBranch->m_nextIntersection = it1; |
| currBranch->m_nextStroke = it2; |
| it2->m_nextIntersection = currInt; |
| it2->m_nextStroke = currBranch; |
| |
| |
| |
| |
| |
| currInt->m_numInter++; |
| it1->m_numInter++; |
| } else if (b.m_nextBranch == i) |
| currBranch->m_nextIntersection = intList.end(); |
| else if (b.m_nextBranch == (std::numeric_limits<UINT>::max)()) { |
| currBranch->m_nextIntersection = intList.end(); |
| currBranch->m_nextStroke = currInt->m_strokeList.end(); |
| } |
| |
| |
| |
| |
| |
| |
| if (i == branchCount - 1 || v[i].m_currInter != v[i + 1].m_currInter) { |
| int j = i; |
| while (v[j].m_strokeIndex < 0 && |
| ((j > 0 && v[j].m_currInter == v[j - 1].m_currInter) || j == 0)) |
| j--; |
| if (v[j].m_strokeIndex >= 0) |
| currInt->m_intersection = |
| m_strokes[v[j].m_strokeIndex]->m_s->getPoint(v[j].m_w); |
| } |
| } |
| |
| for (i = 0; i < m_strokes.size(); i++) m_strokes[i]->m_isNewForFill = false; |
| |
| |
| |
| list<Intersection>::iterator it1; |
| list<IntersectedStroke>::iterator it2; |
| |
| vector<UINT> toBeDeleted; |
| |
| for (it1 = intList.begin(); it1 != intList.end(); it1++) |
| for (it2 = it1->m_strokeList.begin(); it2 != it1->m_strokeList.end(); |
| ++it2) { |
| if (it2->m_edge.m_index < 0 && !it2->m_edge.m_s && |
| (it2->m_edge.m_w0 == 0 || it2->m_edge.m_w0 == 1)) { |
| it2->m_edge.m_s = reconstructAutocloseStroke(intList, it1, it2); |
| if (it2->m_edge.m_s) |
| m_intersectionData.m_autocloseMap[it2->m_edge.m_index] = |
| it2->m_edge.m_s; |
| else |
| toBeDeleted.push_back(it2->m_edge.m_index); |
| } |
| } |
| |
| for (it1 = intList.begin(); it1 != intList.end(); it1++) |
| for (it2 = it1->m_strokeList.begin(); it2 != it1->m_strokeList.end(); |
| ++it2) { |
| if (!it2->m_edge.m_s && it2->m_edge.m_index < 0) { |
| it2->m_edge.m_s = |
| m_intersectionData.m_autocloseMap[it2->m_edge.m_index]; |
| |
| |
| if (!it2->m_edge.m_s) toBeDeleted.push_back(it2->m_edge.m_index); |
| } |
| } |
| |
| for (i = 0; i < toBeDeleted.size(); i++) eraseIntersection(toBeDeleted[i]); |
| |
| m_areValidRegions = false; |
| |
| computeRegions(); |
| |
| } |
| |
| |
| |
| void TVectorImage::Imp::eraseIntersection(int index) { |
| vector<int> autocloseStrokes; |
| doEraseIntersection(index, &autocloseStrokes); |
| |
| for (UINT i = 0; i < autocloseStrokes.size(); i++) { |
| doEraseIntersection(autocloseStrokes[i]); |
| assert(autocloseStrokes[i] < 0); |
| m_intersectionData.m_autocloseMap.erase(autocloseStrokes[i]); |
| } |
| } |
| |
| |
| void findNearestIntersection(list<Intersection> &interList) { |
| list<Intersection>::iterator i1; |
| list<IntersectedStroke>::iterator i2; |
| |
| for (i1 = interList.begin(); i1 != interList.end(); i1++) { |
| for (i2 = (*i1).m_strokeList.begin(); i2 != (*i1).m_strokeList.end(); |
| i2++) { |
| if ((*i2).m_nextIntersection != interList.end()) |
| continue; |
| |
| int versus = (i2->m_gettingOut) ? 1 : -1; |
| double minDelta = (std::numeric_limits<double>::max)(); |
| list<Intersection>::iterator it1, it1Res; |
| list<IntersectedStroke>::iterator it2, it2Res; |
| |
| for (it1 = i1; it1 != interList.end(); ++it1) { |
| if (it1 == i1) |
| it2 = i2, it2++; |
| else |
| it2 = (*it1).m_strokeList.begin(); |
| |
| for (; it2 != (*it1).m_strokeList.end(); ++it2) { |
| if ((*it2).m_edge.m_index == i2->m_edge.m_index && |
| (*it2).m_gettingOut == !i2->m_gettingOut) { |
| double delta = versus * (it2->m_edge.m_w0 - i2->m_edge.m_w0); |
| |
| if (delta > 0 && delta < minDelta) { |
| it1Res = it1; |
| it2Res = it2; |
| minDelta = delta; |
| } |
| } |
| } |
| } |
| |
| if (minDelta != (std::numeric_limits<double>::max)()) { |
| (*it2Res).m_nextIntersection = i1; |
| (*it2Res).m_nextStroke = i2; |
| (*it2Res).m_edge.m_w1 = i2->m_edge.m_w0; |
| (*i2).m_nextIntersection = it1Res; |
| (*i2).m_nextStroke = it2Res; |
| (*i2).m_edge.m_w1 = it2Res->m_edge.m_w0; |
| i1->m_numInter++; |
| it1Res->m_numInter++; |
| } |
| } |
| } |
| } |
| |
| |
| void markDeadIntersections(list<Intersection> &intList, |
| list<Intersection>::iterator it); |
| |
| |
| |
| void inline markDeadIntersectionsRic(list<Intersection> &intList, |
| list<Intersection>::iterator it) { |
| markDeadIntersections(intList, it); |
| } |
| |
| |
| |
| void markDeadIntersections(list<Intersection> &intList, |
| list<Intersection>::iterator it) { |
| list<IntersectedStroke>::iterator it1 = it->m_strokeList.begin(); |
| |
| if (it->m_numInter == 1) { |
| while (it1->m_nextIntersection == intList.end()) it1++; |
| assert(it1 != it->m_strokeList.end()); |
| |
| list<Intersection>::iterator nextInt = it1->m_nextIntersection; |
| list<IntersectedStroke>::iterator nextStroke = it1->m_nextStroke; |
| |
| it->m_numInter = 0; |
| it1->m_nextIntersection = intList.end(); |
| |
| if (nextInt != intList.end() ) { |
| nextInt->m_numInter--; |
| nextStroke->m_nextIntersection = intList.end(); |
| markDeadIntersectionsRic(intList, nextInt); |
| } |
| } else if (it->m_numInter == 2) |
| { |
| while (it1 != it->m_strokeList.end() && |
| it1->m_nextIntersection == intList.end()) |
| it1++; |
| assert(it1 != it->m_strokeList.end()); |
| list<IntersectedStroke>::iterator it2 = it1; |
| it2++; |
| while (it2 != it->m_strokeList.end() && |
| it2->m_nextIntersection == intList.end()) |
| it2++; |
| assert(it2 != it->m_strokeList.end()); |
| |
| if (it1->m_edge.m_s == it2->m_edge.m_s && |
| it1->m_edge.m_w0 == it2->m_edge.m_w0) |
| { |
| list<IntersectedStroke>::iterator iit1, iit2; |
| assert(it1->m_nextIntersection != intList.end() && |
| it2->m_nextIntersection != intList.end()); |
| |
| iit1 = it1->m_nextStroke; |
| iit2 = it2->m_nextStroke; |
| |
| iit2->m_edge.m_w1 = iit1->m_edge.m_w0; |
| iit1->m_edge.m_w1 = iit2->m_edge.m_w0; |
| |
| |
| (*iit1).m_nextStroke = iit2; |
| |
| (*iit2).m_nextStroke = iit1; |
| |
| (*iit1).m_nextIntersection = it2->m_nextIntersection; |
| |
| (*iit2).m_nextIntersection = it1->m_nextIntersection; |
| |
| it->m_numInter = 0; |
| it1->m_nextIntersection = intList.end(); |
| it2->m_nextIntersection = intList.end(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| double nearCrossVal(TStroke *s0, double w0, TStroke *s1, double w1) { |
| double ltot0 = s0->getLength(); |
| double ltot1 = s1->getLength(); |
| double dl = tmin(ltot1, ltot0) / 1000; |
| |
| double crossVal, dl0 = dl, dl1 = dl; |
| |
| TPointD p0, p1; |
| int count = 0; |
| |
| if (w0 == 1.0) dl0 = -dl0; |
| if (w1 == 1.0) dl1 = -dl1; |
| |
| double l0 = s0->getLength(w0) + dl0; |
| double l1 = s1->getLength(w1) + dl1; |
| |
| do { |
| p0 = s0->getSpeed(s0->getParameterAtLength(l0)); |
| p1 = s1->getSpeed(s1->getParameterAtLength(l1)); |
| crossVal = cross(p0, p1); |
| l0 += dl0, l1 += dl1; |
| count++; |
| } while (areAlmostEqual(crossVal, 0.0) && |
| ((dl0 > 0 && l0 < ltot0) || (dl0 < 0 && l0 > 0)) && |
| ((dl1 > 0 && l1 < ltot1) || (dl1 < 0 && l1 > 0))); |
| return crossVal; |
| } |
| |
| |
| |
| inline void insertBranch(Intersection &interList, IntersectedStroke &item, |
| bool gettingOut) { |
| if (item.m_edge.m_w0 != (gettingOut ? 1.0 : 0.0)) { |
| item.m_gettingOut = gettingOut; |
| interList.m_strokeList.push_back(item); |
| } |
| } |
| |
| |
| |
| double getAngle(const TPointD &p0, const TPointD &p1) { |
| double angle1 = 180 * atan2(p0.x, p0.y) / TConsts::pi; |
| double angle2 = 180 * atan2(p1.x, p1.y) / TConsts::pi; |
| |
| if (angle1 < 0) angle1 = 360 + angle1; |
| if (angle2 < 0) angle2 = 360 + angle2; |
| |
| return (angle2 - angle1) < 0 ? 360 + angle2 - angle1 : angle2 - angle1; |
| } |
| |
| |
| |
| |
| double getNearAngle(const TStroke *s1, double w1, bool out1, const TStroke *s2, |
| double w2, bool out2) { |
| bool verse1 = (out1 && w1 < 1) || (!out1 && w1 == 0); |
| bool verse2 = (out2 && w2 < 1) || (!out2 && w2 == 0); |
| double ltot1 = s1->getLength(); |
| double ltot2 = s2->getLength(); |
| double l1 = s1->getLength(w1); |
| double l2 = s2->getLength(w2); |
| double dl = min(ltot1, ltot2) / 1000; |
| double dl1 = verse1 ? dl : -dl; |
| double dl2 = verse2 ? dl : -dl; |
| |
| while (((verse1 && l1 < ltot1) || (!verse1 && l1 > 0)) && |
| ((verse2 && l2 < ltot2) || (!verse2 && l2 > 0))) { |
| l1 += dl1; |
| l2 += dl2; |
| TPointD p1 = (out1 ? 1 : -1) * s1->getSpeed(s1->getParameterAtLength(l1)); |
| TPointD p2 = (out2 ? 1 : -1) * s2->getSpeed(s2->getParameterAtLength(l2)); |
| double angle = getAngle(p1, p2); |
| if (!areAlmostEqual(angle, 0, 1e-9)) return angle; |
| } |
| return 0; |
| } |
| |
| |
| |
| bool makeEdgeIntersection(Intersection &interList, IntersectedStroke &item1, |
| IntersectedStroke &item2, const TPointD &p1a, |
| const TPointD &p1b, const TPointD &p2a, |
| const TPointD &p2b) { |
| double angle1 = getAngle(p1a, p1b); |
| double angle2 = getAngle(p1a, p2a); |
| double angle3 = getAngle(p1a, p2b); |
| double angle; |
| |
| bool eraseP1b = false, eraseP2a = false, eraseP2b = false; |
| |
| if (areAlmostEqual(angle1, 0, 1e-9)) { |
| angle1 = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, true, |
| item1.m_edge.m_s, item1.m_edge.m_w0, false); |
| if (areAlmostEqual(angle1, 1e-9)) eraseP1b = true; |
| } |
| if (areAlmostEqual(angle2, 0, 1e-9)) { |
| angle2 = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, true, |
| item2.m_edge.m_s, item2.m_edge.m_w0, true); |
| if (areAlmostEqual(angle2, 1e-9)) eraseP2a = true; |
| } |
| if (areAlmostEqual(angle3, 0, 1e-9)) { |
| angle3 = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, true, |
| item2.m_edge.m_s, item2.m_edge.m_w0, false); |
| if (areAlmostEqual(angle3, 1e-9)) eraseP2b = true; |
| } |
| |
| if (areAlmostEqual(angle1, angle2, 1e-9)) { |
| angle = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, false, |
| item2.m_edge.m_s, item2.m_edge.m_w0, true); |
| if (angle != 0) { |
| angle2 += angle; |
| if (angle2 > 360) angle2 -= 360; |
| } else |
| eraseP2a = true; |
| } |
| if (areAlmostEqual(angle1, angle3, 1e-9)) { |
| angle = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, false, |
| item2.m_edge.m_s, item2.m_edge.m_w0, false); |
| if (angle != 0) { |
| angle3 += angle; |
| if (angle3 > 360) angle3 -= 360; |
| } else |
| eraseP2b = true; |
| } |
| if (areAlmostEqual(angle2, angle3, 1e-9)) { |
| angle = getNearAngle(item1.m_edge.m_s, item1.m_edge.m_w0, false, |
| item2.m_edge.m_s, item2.m_edge.m_w0, true); |
| if (angle != 0) { |
| angle3 += angle; |
| if (angle3 > 360) angle3 -= 360; |
| } else |
| eraseP2b = true; |
| } |
| |
| int fac = |
| (angle1 < angle2) | ((angle1 < angle3) << 1) | ((angle2 < angle3) << 2); |
| |
| switch (fac) { |
| CASE 0 : |
| insertBranch(interList, item1, true); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| CASE 1 : |
| insertBranch(interList, item1, true); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| CASE 2 : assert(false); |
| CASE 3 : |
| insertBranch(interList, item1, true); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| CASE 4 : |
| insertBranch(interList, item1, true); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| CASE 5 : assert(false); |
| CASE 6 : |
| insertBranch(interList, item1, true); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| CASE 7 : |
| insertBranch(interList, item1, true); |
| if (!eraseP1b) insertBranch(interList, item1, false); |
| if (!eraseP2a) insertBranch(interList, item2, true); |
| if (!eraseP2b) insertBranch(interList, item2, false); |
| DEFAULT: |
| assert(false); |
| } |
| |
| return true; |
| } |
| |
| |
| |
| bool makeIntersection(IntersectionData &intData, const vector<VIStroke *> &s, |
| int ii, int jj, DoublePair inter, int strokeSize, |
| Intersection &interList) { |
| IntersectedStroke item1(intData.m_intList.end(), NULL_ITER), |
| item2(intData.m_intList.end(), NULL_ITER); |
| |
| interList.m_intersection = s[ii]->m_s->getPoint(inter.first); |
| |
| item1.m_edge.m_w0 = inter.first; |
| item2.m_edge.m_w0 = inter.second; |
| |
| if (ii >= 0 && ii < strokeSize) { |
| item1.m_edge.m_s = s[ii]->m_s; |
| item1.m_edge.m_index = ii; |
| } else { |
| if (ii < 0) { |
| item1.m_edge.m_s = intData.m_autocloseMap[ii]; |
| item1.m_edge.m_index = ii; |
| } else { |
| item1.m_edge.m_s = s[ii]->m_s; |
| item1.m_edge.m_index = -(ii + intData.maxAutocloseId * 100000); |
| intData.m_autocloseMap[item1.m_edge.m_index] = item1.m_edge.m_s; |
| } |
| } |
| |
| if (jj >= 0 && jj < strokeSize) { |
| item2.m_edge.m_s = s[jj]->m_s; |
| item2.m_edge.m_index = jj; |
| } else { |
| if (jj < 0) { |
| item2.m_edge.m_s = intData.m_autocloseMap[jj]; |
| item2.m_edge.m_index = jj; |
| } else { |
| item2.m_edge.m_s = s[jj]->m_s; |
| item2.m_edge.m_index = -(jj + intData.maxAutocloseId * 100000); |
| intData.m_autocloseMap[item2.m_edge.m_index] = item2.m_edge.m_s; |
| } |
| } |
| |
| bool reversed = false; |
| |
| TPointD p0, p0b, p1, p1b; |
| |
| bool ret1 = item1.m_edge.m_s->getSpeedTwoValues(item1.m_edge.m_w0, p0, p0b); |
| bool ret2 = item2.m_edge.m_s->getSpeedTwoValues(item2.m_edge.m_w0, p1, p1b); |
| |
| if (ret1 || ret2) |
| return makeEdgeIntersection(interList, item1, item2, p0, p0b, p1, p1b); |
| |
| double crossVal = cross(p0, p1); |
| |
| |
| |
| if (areAlmostEqual(crossVal, 0.0)) { |
| bool endpoint1 = (item1.m_edge.m_w0 == 0.0 || item1.m_edge.m_w0 == 1.0); |
| bool endpoint2 = (item2.m_edge.m_w0 == 0.0 || item2.m_edge.m_w0 == 1.0); |
| if (endpoint1 && endpoint2 && ((p0.x * p1.x >= 0 && p0.y * p1.y >= 0 && |
| item1.m_edge.m_w0 != item2.m_edge.m_w0) || |
| (p0.x * p1.x <= 0 && p0.y * p1.y <= 0 && |
| item1.m_edge.m_w0 == item2.m_edge.m_w0))) |
| |
| { |
| item1.m_gettingOut = (item1.m_edge.m_w0 == 0.0); |
| interList.m_strokeList.push_back(item1); |
| item2.m_gettingOut = (item2.m_edge.m_w0 == 0.0); |
| interList.m_strokeList.push_back(item2); |
| return true; |
| } |
| |
| |
| |
| |
| return makeEdgeIntersection(interList, item1, item2, p0, p0b, p1, p1b); |
| } |
| |
| if (crossVal > 0) |
| reversed = true; |
| |
| |
| if (item1.m_edge.m_w0 != 1.0) { |
| item1.m_gettingOut = true; |
| interList.m_strokeList.push_back(item1); |
| } |
| if (item2.m_edge.m_w0 != (reversed ? 0.0 : 1.0)) { |
| item2.m_gettingOut = !reversed; |
| interList.m_strokeList.push_back(item2); |
| } |
| if (item1.m_edge.m_w0 != 0.0) { |
| item1.m_gettingOut = false; |
| interList.m_strokeList.push_back(item1); |
| } |
| if (item2.m_edge.m_w0 != (reversed ? 1.0 : 0.0)) { |
| item2.m_gettingOut = reversed; |
| interList.m_strokeList.push_back(item2); |
| } |
| |
| return true; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool addAutocloseIntersection(IntersectionData &intData, vector<VIStroke *> &s, |
| int ii, int jj, double w0, double w1, |
| int strokeSize) { |
| list<Intersection>::reverse_iterator rit = intData.m_intList.rbegin(); |
| |
| assert(w0 >= 0.0 && w0 <= 1.0); |
| assert(w1 >= 0.0 && w1 <= 1.0); |
| |
| for (; rit != intData.m_intList.rend(); |
| rit++) |
| |
| { |
| if (rit->m_strokeList.size() < 2) continue; |
| list<IntersectedStroke>::iterator is = rit->m_strokeList.begin(); |
| int s0 = is->m_edge.m_index; |
| if (s0 < 0) continue; |
| double ww0 = is->m_edge.m_w0; |
| is++; |
| if (is->m_edge.m_index == s0 && is->m_edge.m_w0 == ww0) { |
| is++; |
| if (is == rit->m_strokeList.end()) continue; |
| } |
| int s1 = is->m_edge.m_index; |
| if (s1 < 0) continue; |
| double ww1 = is->m_edge.m_w0; |
| |
| if (!((s0 == ii && s1 == jj) || (s0 == jj && s1 == ii))) continue; |
| |
| if (s0 == ii && areAlmostEqual(w0, ww0, 0.1) && |
| areAlmostEqual(w1, ww1, 0.1)) |
| return false; |
| else if (s1 == ii && areAlmostEqual(w0, ww1, 0.1) && |
| areAlmostEqual(w1, ww0, 0.1)) |
| return false; |
| } |
| |
| vector<TPointD> v; |
| v.push_back(s[ii]->m_s->getPoint(w0)); |
| v.push_back(s[jj]->m_s->getPoint(w1)); |
| if (v[0] == v[1]) |
| |
| |
| { |
| addIntersection(intData, s, ii, jj, DoublePair(w0, w1), strokeSize); |
| return true; |
| } |
| |
| |
| for (int i = 0; i < (int)s.size(); i++) |
| if (s[i]->m_s->getChunkCount() == |
| 1) |
| { |
| const TThickQuadratic *q = s[i]->m_s->getChunk(0); |
| |
| if (areAlmostEqual(q->getP0(), v[0], 1e-2) && |
| areAlmostEqual(q->getP2(), v[1], 1e-2) || |
| areAlmostEqual(q->getP0(), v[1], 1e-2) && |
| areAlmostEqual(q->getP2(), v[0], 1e-2)) { |
| return true; |
| addIntersection(intData, s, i, ii, DoublePair(0.0, w0), strokeSize); |
| addIntersection(intData, s, i, jj, DoublePair(1.0, w1), strokeSize); |
| return true; |
| } |
| } |
| |
| s.push_back(new VIStroke(new TStroke(v))); |
| addIntersection(intData, s, s.size() - 1, ii, DoublePair(0.0, w0), |
| strokeSize); |
| addIntersection(intData, s, s.size() - 1, jj, DoublePair(1.0, w1), |
| strokeSize); |
| return true; |
| } |
| |
| |
| |
| double g_autocloseTolerance = c_newAutocloseTolerance; |
| |
| bool makeEndPointConnections(vector<VIStroke *> &s, int ii, bool isIStartPoint, |
| int jj, bool isJStartPoint, |
| IntersectionData &intData, int strokeSize) { |
| double x0 = (isIStartPoint ? 0.0 : 1.0); |
| double x1 = (isJStartPoint ? 0.0 : 1.0); |
| |
| TThickPoint p0 = s[ii]->m_s->getThickPoint(x0); |
| TThickPoint p1 = s[jj]->m_s->getThickPoint(x1); |
| double dist2; |
| |
| dist2 = tdistance2(p0, p1); |
| if (dist2 >= 0 && |
| dist2 <= |
| tmax((g_autocloseTolerance == c_oldAutocloseTolerance) ? 9.09 : 2.0, |
| g_autocloseTolerance * (p0.thick + p1.thick) * |
| (p0.thick + |
| p1.thick))) |
| { |
| if (ii == jj) { |
| TRectD r = s[ii]->m_s->getBBox(); |
| |
| |
| if (fabs(r.x1 - r.x0) < 2 && fabs(r.y1 - r.y0) < 2) return false; |
| } |
| return addAutocloseIntersection(intData, s, ii, jj, x0, x1, strokeSize); |
| } |
| |
| return false; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| double getCurlW(TStroke *s, bool isBegin) |
| |
| |
| |
| |
| { |
| int numChunks = s->getChunkCount(); |
| double dx2, dx1 = 0, dy2, dy1 = 0; |
| |
| for (int i = 0; i < numChunks; i++) { |
| const TQuadratic *q = s->getChunk(isBegin ? i : numChunks - 1 - i); |
| dy2 = q->getP1().y - q->getP0().y; |
| if (dy1 * dy2 < 0) break; |
| dy1 = dy2; |
| dy2 = q->getP2().y - q->getP1().y; |
| if (dy1 * dy2 < 0) break; |
| dy1 = dy2; |
| } |
| if (i == numChunks) return -1; |
| |
| int maxMin0 = isBegin ? i : numChunks - 1 - i; |
| |
| for (int j = 0; j < numChunks; j++) { |
| const TQuadratic *q = s->getChunk(isBegin ? j : numChunks - 1 - j); |
| dx2 = q->getP1().x - q->getP0().x; |
| if (dx1 * dx2 < 0) break; |
| dx1 = dx2; |
| dx2 = q->getP2().x - q->getP1().x; |
| if (dx1 * dx2 < 0) break; |
| dx1 = dx2; |
| } |
| if (j == numChunks) return -1; |
| |
| int maxMin1 = isBegin ? j : numChunks - 1 - j; |
| |
| return getWfromChunkAndT( |
| s, isBegin ? tmax(maxMin0, maxMin1) : tmin(maxMin0, maxMin1), |
| isBegin ? 1.0 : 0.0); |
| } |
| |
| |
| bool lastIsX = false, lastIsY = false; |
| for (int i = 0; i < numChunks; i++) { |
| const TThickQuadratic *q = s->getChunk(isBegin ? i : numChunks - 1 - i); |
| if ((q->getP0().y < q->getP1().y && |
| q->getP2().y < |
| q->getP1().y) || |
| (q->getP0().y > q->getP1().y && q->getP2().y > q->getP1().y)) { |
| double w = getWfromChunkAndT(s, isBegin ? i : numChunks - 1 - i, |
| isBegin ? 1.0 : 0.0); |
| if (lastIsX) |
| return w; |
| lastIsX = false; |
| lastIsY = true; |
| |
| } else if ((q->getP0().x < q->getP1().x && |
| q->getP2().x < |
| q->getP1() |
| .x) || |
| (q->getP0().x > q->getP1().x && q->getP2().x > q->getP1().x)) { |
| double w = getWfromChunkAndT(s, isBegin ? i : numChunks - 1 - i, |
| isBegin ? 1.0 : 0.0); |
| if (lastIsY) |
| return w; |
| lastIsX = true; |
| lastIsY = false; |
| } |
| } |
| |
| return -1; |
| } |
| |
| |
| |
| |
| void makeConnection(vector<VIStroke *> &s, int ii, int jj, bool isBegin, |
| IntersectionData &intData, int strokeSize) { |
| if (s[ii]->m_s->isSelfLoop()) return; |
| |
| double w0 = isBegin ? 0.0 : 1.0; |
| |
| TThickPoint p0 = s[ii]->m_s->getThickPoint(w0); |
| double t, dist2; |
| int index; |
| TStroke sAux, *sComp; |
| |
| if (ii == jj) |
| |
| |
| { |
| double w = getCurlW(s[ii]->m_s, isBegin); |
| if (w == -1) return; |
| |
| split<TStroke>(*(s[ii]->m_s), tmin(isBegin ? 1.0 : 0.0, w), |
| tmax(isBegin ? 1.0 : 0.0, w), sAux); |
| sComp = &sAux; |
| } else |
| sComp = s[jj]->m_s; |
| |
| if (sComp->getNearestChunk(p0, t, index, dist2) && dist2 > 0) { |
| if (ii == jj) { |
| double dummy; |
| s[jj]->m_s->getNearestChunk(sComp->getChunk(index)->getPoint(t), t, index, |
| dummy); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TThickPoint p1 = s[jj]->m_s->getChunk(index)->getThickPoint(t); |
| if (dist2 <= |
| (tmax( |
| (g_autocloseTolerance == c_oldAutocloseTolerance) ? 9.09 : 2.0, |
| (g_autocloseTolerance + 0.7) * (p0.thick + p1.thick) * |
| (p0.thick + p1.thick)))) |
| { |
| |
| |
| double w = getWfromChunkAndT(s[jj]->m_s, index, t); |
| addAutocloseIntersection(intData, s, ii, jj, w0, w, strokeSize); |
| } |
| } |
| } |
| |
| |
| |
| void autoclose(vector<VIStroke *> &s, int ii, int jj, IntersectionData &IntData, |
| int strokeSize) { |
| bool ret1 = false, ret2 = false, ret3 = false, ret4 = false; |
| |
| if (!s[ii]->m_s->isSelfLoop() && !s[jj]->m_s->isSelfLoop()) { |
| ret1 = makeEndPointConnections(s, ii, true, jj, false, IntData, strokeSize); |
| |
| if (ii != jj) { |
| ret2 = |
| makeEndPointConnections(s, ii, true, jj, true, IntData, strokeSize); |
| ret3 = |
| makeEndPointConnections(s, ii, false, jj, true, IntData, strokeSize); |
| ret4 = |
| makeEndPointConnections(s, ii, false, jj, false, IntData, strokeSize); |
| } |
| } |
| |
| if (!ret1 && !ret2) makeConnection(s, ii, jj, true, IntData, strokeSize); |
| if (!ret1 && !ret4) makeConnection(s, jj, ii, false, IntData, strokeSize); |
| if (ii != jj) { |
| if (!ret2 && !ret3) makeConnection(s, jj, ii, true, IntData, strokeSize); |
| if (!ret3 && !ret4) makeConnection(s, ii, jj, false, IntData, strokeSize); |
| } |
| } |
| |
| |
| |
| TPointD inline getTangent(const IntersectedStroke &item) { |
| return (item.m_gettingOut ? 1 : -1) * |
| item.m_edge.m_s->getSpeed(item.m_edge.m_w0, item.m_gettingOut); |
| } |
| |
| |
| |
| void addBranch(IntersectionData &intData, list<IntersectedStroke> &strokeList, |
| const vector<VIStroke *> &s, int ii, double w, int strokeSize, |
| bool gettingOut) { |
| list<IntersectedStroke>::iterator it1, it2; |
| TPointD tanRef, lastTan; |
| |
| IntersectedStroke item(intData.m_intList.end(), strokeList.end()); |
| |
| if (ii < 0) { |
| item.m_edge.m_s = intData.m_autocloseMap[ii]; |
| item.m_edge.m_index = ii; |
| } else { |
| item.m_edge.m_s = s[ii]->m_s; |
| if (ii < strokeSize) |
| item.m_edge.m_index = ii; |
| else { |
| item.m_edge.m_index = -(ii + intData.maxAutocloseId * 100000); |
| intData.m_autocloseMap[item.m_edge.m_index] = item.m_edge.m_s; |
| } |
| } |
| |
| item.m_edge.m_w0 = w; |
| item.m_gettingOut = gettingOut; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| tanRef = getTangent(item); |
| lastTan = getTangent(strokeList.back()); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| it2 = strokeList.end(); |
| it2--; |
| for (it1 = strokeList.begin(); it1 != strokeList.end(); ++it1) { |
| TPointD curTan = getTangent(*it1); |
| double angle0 = getAngle(lastTan, curTan); |
| double angle1 = getAngle(lastTan, tanRef); |
| |
| if (areAlmostEqual(angle1, 0, 1e-8)) { |
| double angle = |
| getNearAngle(it2->m_edge.m_s, it2->m_edge.m_w0, it2->m_gettingOut, |
| item.m_edge.m_s, item.m_edge.m_w0, item.m_gettingOut); |
| angle1 += angle; |
| if (angle1 > 360) angle1 -= 360; |
| } |
| |
| if (areAlmostEqual(angle0, angle1, 1e-8)) { |
| double angle = |
| getNearAngle(it1->m_edge.m_s, it1->m_edge.m_w0, it1->m_gettingOut, |
| item.m_edge.m_s, item.m_edge.m_w0, item.m_gettingOut); |
| angle1 += angle; |
| if (angle1 > 360) angle1 -= 360; |
| } |
| |
| if (angle1 < angle0) { |
| strokeList.insert(it1, item); |
| return; |
| } |
| lastTan = curTan; |
| it2 = it1; |
| } |
| |
| |
| strokeList.push_back(item); |
| } |
| |
| |
| |
| void addBranches(IntersectionData &intData, Intersection &intersection, |
| const vector<VIStroke *> &s, int ii, int jj, |
| DoublePair intersectionPair, int strokeSize) { |
| bool foundS1 = false, foundS2 = false; |
| list<IntersectedStroke>::iterator it; |
| |
| assert(!intersection.m_strokeList.empty()); |
| |
| for (it = intersection.m_strokeList.begin(); |
| it != intersection.m_strokeList.end(); it++) { |
| if ((ii >= 0 && (*it).m_edge.m_s == s[ii]->m_s && |
| it->m_edge.m_w0 == intersectionPair.first) || |
| (ii < 0 && (*it).m_edge.m_index == ii && |
| it->m_edge.m_w0 == intersectionPair.first)) |
| foundS1 = true; |
| if ((jj >= 0 && (*it).m_edge.m_s == s[jj]->m_s && |
| it->m_edge.m_w0 == intersectionPair.second) || |
| (jj < 0 && (*it).m_edge.m_index == jj && |
| it->m_edge.m_w0 == intersectionPair.second)) |
| foundS2 = true; |
| } |
| |
| if (foundS1 && foundS2) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return; |
| } |
| |
| if (!foundS1) { |
| if (intersectionPair.first != 1) |
| addBranch(intData, intersection.m_strokeList, s, ii, |
| intersectionPair.first, strokeSize, true); |
| if (intersectionPair.first != 0) |
| addBranch(intData, intersection.m_strokeList, s, ii, |
| intersectionPair.first, strokeSize, false); |
| |
| } |
| if (!foundS2) { |
| if (intersectionPair.second != 1) |
| addBranch(intData, intersection.m_strokeList, s, jj, |
| intersectionPair.second, strokeSize, true); |
| if (intersectionPair.second != 0) |
| addBranch(intData, intersection.m_strokeList, s, jj, |
| intersectionPair.second, strokeSize, false); |
| |
| |
| } |
| } |
| |
| |
| |
| void addIntersections(IntersectionData &intData, const vector<VIStroke *> &s, |
| int ii, int jj, vector<DoublePair> &intersections, |
| int strokeSize) { |
| for (int k = 0; k < (int)intersections.size(); k++) { |
| if (ii >= strokeSize && (areAlmostEqual(intersections[k].first, 0.0) || |
| areAlmostEqual(intersections[k].first, 1.0))) |
| continue; |
| if (jj >= strokeSize && (areAlmostEqual(intersections[k].second, 0.0) || |
| areAlmostEqual(intersections[k].second, 1.0))) |
| continue; |
| |
| addIntersection(intData, s, ii, jj, intersections[k], strokeSize); |
| } |
| } |
| |
| |
| |
| inline double truncate(double x) { |
| x += 1.0; |
| unsigned long *l = (unsigned long *)&x; |
| |
| |
| l[0] &= 0xFFE00000; |
| |
| l[1] &= 0xFFE00000; |
| |
| |
| return x - 1.0; |
| } |
| |
| |
| |
| void addIntersection(IntersectionData &intData, const vector<VIStroke *> &s, |
| int ii, int jj, DoublePair intersection, int strokeSize) { |
| list<Intersection>::iterator it; |
| TPointD p; |
| |
| |
| |
| |
| |
| |
| |
| |
| if (areAlmostEqual(intersection.first, 0.0, 1e-8)) |
| intersection.first = 0.0; |
| else if (areAlmostEqual(intersection.first, 1.0, 1e-8)) |
| intersection.first = 1.0; |
| |
| if (areAlmostEqual(intersection.second, 0.0, 1e-8)) |
| intersection.second = 0.0; |
| else if (areAlmostEqual(intersection.second, 1.0, 1e-8)) |
| intersection.second = 1.0; |
| |
| p = s[ii]->m_s->getPoint(intersection.first); |
| |
| for (it = intData.m_intList.begin(); it != intData.m_intList.end(); it++) |
| if ((*it).m_intersection == |
| p) |
| |
| { |
| addBranches(intData, *it, s, ii, jj, intersection, strokeSize); |
| return; |
| } |
| |
| intData.m_intList.push_back(Intersection()); |
| |
| if (!makeIntersection(intData, s, ii, jj, intersection, strokeSize, |
| intData.m_intList.back())) { |
| list<Intersection>::iterator it = intData.m_intList.begin(); |
| advance(it, intData.m_intList.size() - 1); |
| |
| intData.m_intList.erase(it); |
| } |
| } |
| |
| |
| |
| void TVectorImage::Imp::findIntersections() { |
| vector<VIStroke *> &strokeArray = m_strokes; |
| IntersectionData &intData = m_intersectionData; |
| int strokeSize = (int)strokeArray.size(); |
| int i, j; |
| |
| assert(intData.m_intersectedStrokeArray.empty()); |
| |
| intData.maxAutocloseId++; |
| |
| map<int, TStroke *>::iterator it, it_b = intData.m_autocloseMap.begin(); |
| map<int, TStroke *>::iterator it_e = intData.m_autocloseMap.end(); |
| |
| |
| for (i = 0; i < strokeSize; i++) { |
| TStroke *s1 = strokeArray[i]->m_s; |
| if (!strokeArray[i]->m_isNewForFill || strokeArray[i]->m_isPoint) continue; |
| TRectD bBox = s1->getBBox(); |
| double thick2 = s1->getThickPoint(0).thick * 2; |
| if (bBox.getLx() <= thick2 && bBox.getLy() <= thick2) { |
| strokeArray[i]->m_isPoint = true; |
| continue; |
| } |
| for (int j = 0; j < (int)s1->getControlPointCount(); j++) { |
| TThickPoint p = s1->getControlPoint(j); |
| s1->setControlPoint(j, myRound(p)); |
| } |
| |
| for (it = it_b; it != it_e; ++it) { |
| TStroke *s2 = it->second; |
| vector<DoublePair> parIntersections; |
| if (intersect(s1, s2, parIntersections, true)) |
| addIntersections(intData, strokeArray, i, it->first, parIntersections, |
| strokeSize); |
| } |
| } |
| |
| |
| |
| for (i = 0; i < strokeSize; i++) { |
| TStroke *s1 = strokeArray[i]->m_s; |
| if (strokeArray[i]->m_isPoint) continue; |
| for (j = i; j < strokeSize |
| |
| ; |
| j++) { |
| TStroke *s2 = strokeArray[j]->m_s; |
| if (strokeArray[j]->m_isPoint) continue; |
| if (!(strokeArray[i]->m_isNewForFill || strokeArray[j]->m_isNewForFill)) |
| continue; |
| vector<DoublePair> parIntersections; |
| if (s1->getBBox().overlaps(s2->getBBox())) { |
| UINT size = intData.m_intList.size(); |
| |
| if (intersect(s1, s2, parIntersections, false)) { |
| |
| |
| addIntersections(intData, strokeArray, i, j, parIntersections, |
| strokeSize); |
| } |
| |
| if (!strokeArray[i]->m_isNewForFill && |
| size != intData.m_intList.size() && |
| !strokeArray[i]->m_edgeList.empty()) |
| { |
| intData.m_intersectedStrokeArray.push_back(IntersectedStrokeEdges(i)); |
| list<TEdge *> &_list = |
| intData.m_intersectedStrokeArray.back().m_edgeList; |
| list<TEdge *>::const_iterator it; |
| for (it = strokeArray[i]->m_edgeList.begin(); |
| it != strokeArray[i]->m_edgeList.end(); ++it) |
| _list.push_back(new TEdge(**it, false)); |
| } |
| } |
| } |
| |
| } |
| |
| for (i = 0; i < strokeSize; i++) { |
| TStroke *s1 = strokeArray[i]->m_s; |
| if (strokeArray[i]->m_isPoint) continue; |
| for (j = i; j < strokeSize; j++) { |
| TStroke *s2 = strokeArray[j]->m_s; |
| if (strokeArray[j]->m_isPoint) continue; |
| if (!(strokeArray[i]->m_isNewForFill || strokeArray[j]->m_isNewForFill)) |
| continue; |
| if (s1->getBBox().overlaps(s2->getBBox())) |
| autoclose(strokeArray, i, j, intData, strokeSize); |
| } |
| strokeArray[i]->m_isNewForFill = false; |
| } |
| |
| for (i = 0; i < strokeSize; i++) { |
| list<TEdge *>::iterator it, it_b = strokeArray[i]->m_edgeList.begin(), |
| it_e = strokeArray[i]->m_edgeList.end(); |
| for (it = it_b; it != it_e; ++it) |
| if ((*it)->m_toBeDeleted == 1) delete *it; |
| |
| strokeArray[i]->m_edgeList.clear(); |
| } |
| |
| |
| |
| for (i = strokeSize; i < (int)strokeArray.size(); ++i) { |
| TStroke *s1 = strokeArray[i]->m_s; |
| |
| for (j = i + 1; j < (int)strokeArray.size(); |
| ++j) |
| { |
| TStroke *s2 = strokeArray[j]->m_s; |
| vector<DoublePair> parIntersections; |
| if (intersect(s1, s2, parIntersections, true)) |
| addIntersections(intData, strokeArray, i, j, parIntersections, |
| strokeSize); |
| } |
| for (j = 0; j < strokeSize; ++j) |
| { |
| if (strokeArray[j]->m_isPoint) continue; |
| TStroke *s2 = strokeArray[j]->m_s; |
| vector<DoublePair> parIntersections; |
| if (intersect(s1, s2, parIntersections, true)) |
| addIntersections(intData, strokeArray, i, j, parIntersections, |
| strokeSize); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| int TVectorImage::Imp::computeIntersections() { |
| list<Intersection>::iterator it1; |
| list<IntersectedStroke>::iterator it2; |
| IntersectionData &intData = m_intersectionData; |
| int strokeSize = (int)m_strokes.size(); |
| |
| findIntersections(); |
| |
| findNearestIntersection(intData.m_intList); |
| |
| |
| |
| eraseDeadIntersections(); |
| |
| for (it1 = intData.m_intList.begin(); it1 != intData.m_intList.end(); it1++) |
| markDeadIntersections(intData.m_intList, it1); |
| |
| |
| return strokeSize; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TRegion *findRegion(list<Intersection> &intList, |
| list<Intersection>::iterator it1, |
| list<IntersectedStroke>::iterator it2) { |
| TRegion *r = new TRegion(); |
| int currStyle = 0; |
| |
| list<IntersectedStroke>::iterator itStart = it2; |
| list<Intersection>::iterator nextIt1; |
| list<IntersectedStroke>::iterator nextIt2; |
| |
| |
| while (!it2->m_visited) { |
| it2->m_visited = true; |
| |
| |
| |
| do { |
| it2++; |
| if (it2 == |
| it1->m_strokeList.end()) |
| it2 = it1->m_strokeList.begin(); |
| } while (it2->m_nextIntersection == intList.end()); |
| |
| nextIt1 = it2->m_nextIntersection; |
| nextIt2 = it2->m_nextStroke; |
| |
| |
| if (it2->m_edge.m_styleId != 0) { |
| if (currStyle == 0) |
| currStyle = it2->m_edge.m_styleId; |
| else if (it2->m_edge.m_styleId != currStyle) { |
| currStyle = it2->m_edge.m_styleId; |
| for (UINT i = 0; i < r->getEdgeCount(); i++) |
| r->getEdge(i)->m_styleId = currStyle; |
| } |
| } else |
| it2->m_edge.m_styleId = currStyle; |
| |
| |
| r->addEdge(&it2->m_edge); |
| |
| if (nextIt2 == itStart) return r; |
| |
| it1 = nextIt1; |
| it2 = nextIt2; |
| } |
| |
| delete r; |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool isValidArea(const vector<TRegion *> ®ions, const TRegion &r) { |
| double area = 0.0; |
| TPointD p, pOld ; |
| int pointAdded = 0; |
| |
| int size = r.getEdgeCount(); |
| |
| if (size == 0) return false; |
| |
| |
| |
| |
| int firstControlPoint; |
| int lastControlPoint; |
| |
| TEdge *e = r.getEdge(size - 1); |
| pOld = e->m_s->getPoint(e->m_w1); |
| |
| for (int i = 0; i < size; i++) { |
| TEdge *e = r.getEdge(i); |
| TStroke *s = e->m_s; |
| firstControlPoint = s->getControlPointIndexAfterParameter(e->m_w0); |
| lastControlPoint = s->getControlPointIndexAfterParameter(e->m_w1); |
| |
| p = s->getPoint(e->m_w0); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| if (firstControlPoint <= lastControlPoint) { |
| if (firstControlPoint & 0x1) firstControlPoint++; |
| if (lastControlPoint - firstControlPoint <= |
| 2) |
| { |
| p = s->getPoint(0.333333 * e->m_w0 + 0.666666 * e->m_w1); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| p = s->getPoint(0.666666 * e->m_w0 + 0.333333 * e->m_w1); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| } else |
| for (int j = firstControlPoint; j < lastControlPoint; j += 2) { |
| p = s->getControlPoint(j); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| } |
| } else { |
| firstControlPoint--; |
| lastControlPoint--; |
| if (firstControlPoint & 0x1) firstControlPoint--; |
| if (firstControlPoint - lastControlPoint <= |
| 2) |
| { |
| p = s->getPoint(0.333333 * e->m_w0 + 0.666666 * e->m_w1); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| p = s->getPoint(0.666666 * e->m_w0 + 0.333333 * e->m_w1); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| } else |
| for (int j = firstControlPoint; j > lastControlPoint; j -= 2) { |
| p = s->getControlPoint(j); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| } |
| } |
| p = s->getPoint(e->m_w1); |
| area += (p.y + pOld.y) * (pOld.x - p.x); |
| pOld = p; |
| pointAdded++; |
| } |
| assert(pointAdded >= 4); |
| |
| return area > 0.5; |
| } |
| |
| |
| |
| void transferColors(const list<TEdge *> &oldList, const list<TEdge *> &newList, |
| bool isStrokeChanged, bool isFlipped, bool overwriteColor); |
| |
| |
| void printStrokes1(vector<VIStroke *> &v, int size) { |
| UINT i = 0; |
| ofstream of("C:\\temp\\strokes.txt"); |
| |
| for (i = 0; i < (UINT)size; i++) { |
| TStroke *s = v[i]->m_s; |
| of << "***stroke " << i << endl; |
| for (UINT j = 0; j < (UINT)s->getChunkCount(); j++) { |
| const TThickQuadratic *q = s->getChunk(j); |
| |
| of << " s0 " << q->getP0() << endl; |
| of << " s1 " << q->getP1() << endl; |
| of << " s2 " << q->getP2() << endl; |
| of << "****** " << endl; |
| } |
| of << endl; |
| } |
| for (i = size; i < v.size(); i++) { |
| TStroke *s = v[i]->m_s; |
| of << "***Autostroke " << i << endl; |
| of << "s0 " << s->getPoint(0.0) << endl; |
| of << "s1 " << s->getPoint(1.0) << endl; |
| of << endl; |
| } |
| } |
| |
| |
| |
| static void printTime(TStopWatch &sw, string name) { |
| ostringstream ss; |
| ss << name << " : "; |
| sw.print(ss); |
| ss << '\n' << '\0'; |
| string s(ss.str()); |
| |
| } |
| |
| |
| void printStrokes1(vector<VIStroke *> &v, int size); |
| |
| |
| int TVectorImage::Imp::computeRegions() { |
| |
| TStopWatch stopWatch; |
| stopWatch.start(true); |
| |
| |
| if (!m_computeRegions) return 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| g_autocloseTolerance = m_autocloseTolerance; |
| |
| |
| clearPointerContainer(m_regions); |
| m_regions.clear(); |
| |
| |
| if (m_strokes.empty()) { |
| |
| stopWatch.stop(); |
| |
| return 0; |
| } |
| |
| |
| m_intersectionData.m_computedAlmostOnce = true; |
| list<Intersection> &intList = m_intersectionData.m_intList; |
| cleanIntersectionMarks(intList); |
| |
| |
| int added = 0, notAdded = 0; |
| int strokeSize; |
| strokeSize = computeIntersections(); |
| |
| list<Intersection>::iterator it1; |
| list<IntersectedStroke>::iterator it2; |
| |
| for (it1 = intList.begin(); it1 != intList.end(); it1++) |
| for (it2 = (*it1).m_strokeList.begin(); it2 != (*it1).m_strokeList.end(); |
| it2++) |
| it2->m_edge.m_r = 0; |
| |
| for (it1 = intList.begin(); it1 != intList.end(); it1++) { |
| |
| if (it1->m_numInter == 0) continue; |
| |
| for (it2 = it1->m_strokeList.begin(); it2 != it1->m_strokeList.end(); |
| it2++) { |
| TRegion *region; |
| |
| |
| |
| if (it2->m_nextIntersection == intList.end()) continue; |
| |
| |
| |
| if (!it2->m_visited && (region = findRegion(intList, it1, it2))) { |
| |
| if (isValidArea(m_regions, *region)) { |
| added++; |
| addRegion(m_regions, region); |
| |
| |
| for (UINT i = 0; i < region->getEdgeCount(); i++) { |
| TEdge *e = region->getEdge(i); |
| e->m_r = region; |
| if (e->m_index >= 0) m_strokes[e->m_index]->addEdge(e); |
| } |
| } else |
| { |
| notAdded++; |
| delete region; |
| } |
| } |
| } |
| } |
| |
| if (!m_notIntersectingStrokes) { |
| UINT i; |
| for (i = 0; i < m_intersectionData.m_intersectedStrokeArray.size(); i++) { |
| if (!m_strokes[m_intersectionData.m_intersectedStrokeArray[i].m_index] |
| ->m_edgeList.empty()) |
| transferColors( |
| m_intersectionData.m_intersectedStrokeArray[i].m_edgeList, |
| m_strokes[m_intersectionData.m_intersectedStrokeArray[i].m_index] |
| ->m_edgeList, |
| false, false, true); |
| clearPointerContainer( |
| m_intersectionData.m_intersectedStrokeArray[i].m_edgeList); |
| m_intersectionData.m_intersectedStrokeArray[i].m_edgeList.clear(); |
| } |
| m_intersectionData.m_intersectedStrokeArray.clear(); |
| } |
| |
| assert(m_intersectionData.m_intersectedStrokeArray.empty()); |
| |
| |
| vector<VIStroke *>::iterator it = m_strokes.begin(); |
| advance(it, strokeSize); |
| m_strokes.erase(it, m_strokes.end()); |
| |
| m_areValidRegions = true; |
| |
| |
| |
| |
| |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |