1 
2 
3 #include "autofill.h"
4 #include "tregion.h"
5 #include "tgeometry.h"
6 #include "tstroke.h"
7 #include "toonz4.6/tmacro.h"
8 #include <QMap>
9 #include <QPair>
10 #include <QList>
11 
12 namespace {
13 
14 #define BORDER_TOO 1
15 #define NO_BORDER 0
16 #define DIM_TRESH 0.00005
17 #define AMB_TRESH 130000
18 #define MIN_SIZE 20
19 
20 //==============================================================================================
21 
22 struct Region {
23   double m_area, m_perimeter;
24   TPointD m_barycentre;
25   TDimensionD m_size;
26   int m_match, m_styleId;
27   TRegion *m_region;
28 
Region__anond5d5ae070111::Region29   Region()
30       : m_area(0)
31       , m_perimeter(0)
32       , m_barycentre(0, 0)
33       , m_size(0, 0)
34       , m_match(-1)
35       , m_styleId(0)
36       , m_region(0) {}
37 };
38 
39 struct MatchingProbs {
40   int m_from, m_to;
41   int m_perimeterProb, m_areaProb, m_barycenterProb;
42   bool m_overlappingArea, m_matched;
43 
MatchingProbs__anond5d5ae070111::MatchingProbs44   MatchingProbs()
45       : m_from(0)
46       , m_to(0)
47       , m_perimeterProb(0)
48       , m_areaProb(0)
49       , m_barycenterProb(0)
50       , m_overlappingArea(false)
51       , m_matched(false) {}
52 };
53 
54 typedef QMap<int, Region> RegionDataList;
55 
56 static RegionDataList regionsReference, regionsWork;
57 static TPointD referenceB(0, 0);
58 static TPointD workB(0, 0);
59 
60 //==============================================================================================
61 
62 class AreasAndPerimeterFormula final : public TRegionFeatureFormula {
63   double m_signedArea, m_perimeter;
64 
65 public:
AreasAndPerimeterFormula()66   AreasAndPerimeterFormula() : m_signedArea(0), m_perimeter(0) {}
67 
~AreasAndPerimeterFormula()68   ~AreasAndPerimeterFormula() {}
69 
update(const TPointD & p1,const TPointD & p2)70   void update(const TPointD &p1, const TPointD &p2) override {
71     m_perimeter += norm(p2 - p1);
72     m_signedArea += ((p1.x * p2.y) - (p2.x * p1.y)) * 0.5;
73   }
74 
getPerimeter()75   double getPerimeter() { return m_perimeter; }
getSignedArea()76   double getSignedArea() { return m_signedArea; }
getArea()77   double getArea() { return fabs(m_signedArea); }
78 };
79 
80 //---------------------------------------------------------------------------------------------
81 
82 class CentroidFormula final : public TRegionFeatureFormula {
83   TPointD m_centroid;
84   double m_signedArea;
85 
86 public:
CentroidFormula()87   CentroidFormula() : m_centroid(), m_signedArea(0) {}
88 
~CentroidFormula()89   ~CentroidFormula() {}
90 
update(const TPointD & p1,const TPointD & p2)91   void update(const TPointD &p1, const TPointD &p2) override {
92     double factor = ((p1.x * p2.y) - (p2.x * p1.y));
93     m_centroid.x += (p1.x + p2.x) * factor;
94     m_centroid.y += (p1.y + p2.y) * factor;
95   }
96 
setSignedArea(double signedArea)97   void setSignedArea(double signedArea) { m_signedArea = signedArea; }
getCentroid()98   TPointD getCentroid() { return (1 / (6 * m_signedArea)) * m_centroid; }
99 };
100 
101 //---------------------------------------------------------------------------------------------
102 
match(std::vector<MatchingProbs> & probsVector,int & from,int & to)103 int match(std::vector<MatchingProbs> &probsVector, int &from, int &to) {
104   int i = 0, maxProb = 0;
105   bool overlappingArea = false;
106   std::vector<MatchingProbs>::iterator it, matchedProbs;
107   for (it = probsVector.begin(); it != probsVector.end(); it++) {
108     MatchingProbs probs = *it;
109     if (probs.m_matched) continue;
110     int probValue =
111         probs.m_areaProb * probs.m_barycenterProb * probs.m_perimeterProb;
112     if ((!overlappingArea &&
113          (maxProb < probValue || probs.m_overlappingArea)) ||
114         (overlappingArea && maxProb < probValue && probs.m_overlappingArea)) {
115       overlappingArea = probs.m_overlappingArea;
116       maxProb         = probValue;
117       from            = probs.m_from;
118       to              = probs.m_to;
119       matchedProbs    = it;
120     }
121   }
122   if (maxProb) matchedProbs->m_matched = true;
123   return maxProb;
124 }
125 
126 //---------------------------------------------------------------------------------------------
127 
assignProbs(std::vector<MatchingProbs> & probVector,const Region & reference,const Region & work,int from,int to)128 void assignProbs(std::vector<MatchingProbs> &probVector,
129                  const Region &reference, const Region &work, int from,
130                  int to) {
131   double delta_posx1, delta_posy1, delta_posx2, delta_posy2;
132   int delta_area, delta_per;
133   double delta_pos, delta_pos_max;
134 
135   MatchingProbs probs;
136   probs.m_from = from;
137   probs.m_to   = to;
138 
139   TRegion *refRegion  = reference.m_region;
140   TRegion *workRegion = work.m_region;
141   probs.m_overlappingArea =
142       refRegion->getBBox().overlaps(workRegion->getBBox());
143 
144   delta_posx1 = reference.m_barycentre.x / reference.m_area - referenceB.x;
145   delta_posy1 = reference.m_barycentre.y / reference.m_area - referenceB.y;
146 
147   delta_posx2 = work.m_barycentre.x / work.m_area - workB.x;
148   delta_posy2 = work.m_barycentre.y / work.m_area - workB.y;
149 
150   // Cosi' calcolo il modulo della differenza
151 
152   delta_pos = sqrt((delta_posx2 - delta_posx1) * (delta_posx2 - delta_posx1) +
153                    (delta_posy2 - delta_posy1) * (delta_posy2 - delta_posy1));
154   delta_pos_max = sqrt((double)(work.m_size.lx * work.m_size.lx +
155                                 work.m_size.ly * work.m_size.ly));
156 
157   probs.m_barycenterProb = tround(1000 * (1 - (delta_pos / delta_pos_max)));
158 
159   delta_area = std::abs(reference.m_area - work.m_area);
160 
161   probs.m_areaProb = tround(
162       1000 * (1 - ((double)delta_area / (reference.m_area + work.m_area))));
163 
164   delta_per             = std::abs(reference.m_perimeter - work.m_perimeter);
165   probs.m_perimeterProb = tround(
166       1000 *
167       (1 - ((double)delta_per / (reference.m_perimeter + work.m_perimeter))));
168   probVector.push_back(probs);
169 }
170 
171 //----------------------------------------------------------------------------------------------
172 
scanRegion(TRegion * reg,int regionIndex,RegionDataList & rlst,const TRectD & selectingRect)173 void scanRegion(TRegion *reg, int regionIndex, RegionDataList &rlst,
174                 const TRectD &selectingRect) {
175   assert(!rlst.contains(regionIndex));
176   if (!selectingRect.contains(reg->getBBox())) return;
177 
178   AreasAndPerimeterFormula areasAndPerimeter;
179   CentroidFormula centroid;
180   computeRegionFeature(*reg, areasAndPerimeter);
181   computeRegionFeature(*reg, centroid);
182   centroid.setSignedArea(areasAndPerimeter.getSignedArea());
183 
184   Region regionData;
185   regionData.m_area       = areasAndPerimeter.getArea();
186   regionData.m_perimeter  = areasAndPerimeter.getPerimeter();
187   regionData.m_barycentre = centroid.getCentroid() * regionData.m_area;
188   regionData.m_size       = reg->getBBox().getSize();
189   UINT i, subRegCount = reg->getSubregionCount();
190   for (i = 0; i < subRegCount; i++) {
191     TRegion *subReg = reg->getSubregion(i);
192     AreasAndPerimeterFormula subAreasAndPerimeter;
193     CentroidFormula subCentroid;
194     computeRegionFeature(*subReg, subAreasAndPerimeter);
195     computeRegionFeature(*subReg, subCentroid);
196     subCentroid.setSignedArea(subAreasAndPerimeter.getSignedArea());
197 
198     regionData.m_area -= subAreasAndPerimeter.getArea();
199     regionData.m_barycentre -=
200         subCentroid.getCentroid() * subAreasAndPerimeter.getArea();
201   }
202   regionData.m_barycentre.x /= regionData.m_area;
203   regionData.m_barycentre.y /= regionData.m_area;
204   regionData.m_styleId = reg->getStyle();
205   regionData.m_region  = reg;
206 
207   rlst[regionIndex] = regionData;
208 }
209 
210 //----------------------------------------------------------------------------------------------
211 
scanSubRegion(TRegion * region,int & index,RegionDataList & rlst,const TRectD & selectingRect)212 void scanSubRegion(TRegion *region, int &index, RegionDataList &rlst,
213                    const TRectD &selectingRect) {
214   scanRegion(region, index, rlst, selectingRect);
215   index++;
216   int j, subRegionCount = region->getSubregionCount();
217   for (j = 0; j < subRegionCount; j++) {
218     TRegion *subRegion = region->getSubregion(j);
219     scanSubRegion(subRegion, index, rlst, selectingRect);
220   }
221 }
222 
223 //----------------------------------------------------------------------------------------------
224 
contains(TRegion * container,TRegion * contained)225 bool contains(TRegion *container, TRegion *contained) {
226   if (!(container->getBBox().contains(contained->getBBox()))) return false;
227 
228   for (UINT i = 0; i < contained->getEdgeCount(); i++)
229     for (UINT j = 0; j < container->getEdgeCount(); j++)
230       if (*contained->getEdge(i) == *container->getEdge(j)) return false;
231   for (UINT i = 0; i < contained->getEdgeCount(); i++) {
232     TEdge *e = contained->getEdge(i);
233     if (!container->contains(e->m_s->getThickPoint(e->m_w0))) return false;
234     if (!container->contains(e->m_s->getThickPoint((e->m_w0 + e->m_w1) / 2.0)))
235       return false;
236     if (!container->contains(e->m_s->getThickPoint(e->m_w1))) return false;
237   }
238   return true;
239 }
240 
241 }  // namespace
242 
243 //==============================================================================================
244 
rect_autofill_learn(const TVectorImageP & imgToLearn,const TRectD & rect)245 void rect_autofill_learn(const TVectorImageP &imgToLearn, const TRectD &rect)
246 
247 {
248   if (rect.getLx() * rect.getLy() < MIN_SIZE) return;
249 
250   double pbx, pby;
251   double totalArea = 0;
252   pbx = pby = 0;
253 
254   if (!regionsReference.isEmpty()) regionsReference.clear();
255 
256   int i, index = 0, regionCount = imgToLearn->getRegionCount();
257   for (i = 0; i < regionCount; i++) {
258     TRegion *region = imgToLearn->getRegion(i);
259     if (rect.contains(region->getBBox())) {
260       scanRegion(region, index, regionsReference, rect);
261       index++;
262     }
263     int j, subRegionCount = region->getSubregionCount();
264     for (j = 0; j < subRegionCount; j++) {
265       TRegion *subRegion = region->getSubregion(j);
266       if (rect.contains(subRegion->getBBox()))
267         scanSubRegion(subRegion, index, regionsReference, rect);
268     }
269   }
270 
271   QMap<int, Region>::Iterator it;
272   for (it = regionsReference.begin(); it != regionsReference.end(); it++) {
273     pbx += it.value().m_barycentre.x;
274     pby += it.value().m_barycentre.y;
275     totalArea += it.value().m_area;
276   }
277 
278   if (totalArea > 0)
279     referenceB = TPointD(pbx / totalArea, pby / totalArea);
280   else
281     referenceB = TPointD(0.0, 0.0);
282 }
283 
284 //----------------------------------------------------------------------------
285 
rect_autofill_apply(const TVectorImageP & imgToApply,const TRectD & rect,bool selective)286 bool rect_autofill_apply(const TVectorImageP &imgToApply, const TRectD &rect,
287                          bool selective) {
288   if (rect.getLx() * rect.getLy() < MIN_SIZE) return false;
289 
290   if (regionsReference.size() <= 0) return false;
291 
292   double pbx, pby;
293   double totalArea = 0;
294   pbx = pby = 0.0;
295 
296   if (!regionsWork.isEmpty()) regionsWork.clear();
297 
298   int i, index = 0, regionCount = imgToApply->getRegionCount();
299   for (i = 0; i < regionCount; i++) {
300     TRegion *region = imgToApply->getRegion(i);
301     TRectD bbox     = region->getBBox();
302     if (rect.contains(bbox)) {
303       scanRegion(region, index, regionsWork, rect);
304       index++;
305     }
306     int j, subRegionCount = region->getSubregionCount();
307     for (j = 0; j < subRegionCount; j++) {
308       TRegion *subRegion = region->getSubregion(j);
309       if (rect.contains(subRegion->getBBox()))
310         scanSubRegion(subRegion, index, regionsWork, rect);
311     }
312   }
313 
314   if (regionsWork.size() <= 0) return false;
315 
316   QMap<int, Region>::Iterator it;
317   for (it = regionsWork.begin(); it != regionsWork.end(); it++) {
318     pbx += it.value().m_barycentre.x;
319     pby += it.value().m_barycentre.y;
320     totalArea += it.value().m_area;
321   }
322 
323   workB = TPointD(pbx / totalArea, pby / totalArea);
324 
325   std::vector<MatchingProbs> probVector;
326 
327   RegionDataList::Iterator refIt, workIt;
328   for (refIt = regionsReference.begin(); refIt != regionsReference.end();
329        refIt++)
330     for (workIt = regionsWork.begin(); workIt != regionsWork.end(); workIt++)
331       assignProbs(probVector, refIt.value(), workIt.value(), refIt.key(),
332                   workIt.key());
333 
334   bool filledRegions = false;
335   for (refIt = regionsReference.begin(); refIt != regionsReference.end();
336        refIt++) {
337     int to = 0, from = 0;
338     int valore = 0;
339     do
340       valore = match(probVector, from, to);
341     while ((regionsWork[to].m_match != -1 ||
342             regionsReference[from].m_match != -1) &&
343            valore > 0);
344     if (valore > AMB_TRESH) {
345       regionsWork[to].m_match        = from;
346       regionsReference[from].m_match = to;
347       regionsWork[to].m_styleId      = regionsReference[from].m_styleId;
348       TRegion *reg                   = regionsWork[to].m_region;
349       if (reg && (!selective || (selective && reg->getStyle() == 0))) {
350         reg->setStyle(regionsWork[to].m_styleId);
351         filledRegions = true;
352       }
353     }
354   }
355   return filledRegions;
356 }
357 
358 //----------------------------------------------------------------------------
359 
stroke_autofill_learn(const TVectorImageP & imgToLearn,TStroke * stroke)360 void stroke_autofill_learn(const TVectorImageP &imgToLearn, TStroke *stroke) {
361   if (!imgToLearn || !stroke || stroke->getControlPointCount() == 0) return;
362   TVectorImage appImg;
363   TStroke *appStroke = new TStroke(*stroke);
364   appImg.addStroke(appStroke);
365   appImg.findRegions();
366 
367   double pbx, pby;
368   double totalArea = 0;
369   pbx = pby = 0;
370 
371   if (!regionsReference.isEmpty()) regionsReference.clear();
372 
373   int i, j, index = 0;
374 
375   for (i = 0; i < (int)imgToLearn->getRegionCount(); i++) {
376     TRegion *currentRegion = imgToLearn->getRegion(i);
377     for (j = 0; j < (int)appImg.getRegionCount(); j++) {
378       TRegion *region = appImg.getRegion(j);
379       if (contains(region, currentRegion)) {
380         scanRegion(currentRegion, index, regionsReference, region->getBBox());
381         index++;
382         int k, subRegionCount = currentRegion->getSubregionCount();
383         for (k = 0; k < subRegionCount; k++) {
384           TRegion *subRegion = currentRegion->getSubregion(k);
385           if (contains(region, subRegion))
386             scanSubRegion(subRegion, index, regionsReference,
387                           region->getBBox());
388         }
389       }
390     }
391   }
392 
393   QMap<int, Region>::Iterator it;
394   for (it = regionsReference.begin(); it != regionsReference.end(); it++) {
395     pbx += it.value().m_barycentre.x;
396     pby += it.value().m_barycentre.y;
397     totalArea += it.value().m_area;
398   }
399 
400   if (totalArea > 0)
401     referenceB = TPointD(pbx / totalArea, pby / totalArea);
402   else
403     referenceB = TPointD(0.0, 0.0);
404 }
405 
406 //----------------------------------------------------------------------------
407 
stroke_autofill_apply(const TVectorImageP & imgToApply,TStroke * stroke,bool selective)408 bool stroke_autofill_apply(const TVectorImageP &imgToApply, TStroke *stroke,
409                            bool selective) {
410   if (!imgToApply || !stroke || stroke->getControlPointCount() == 0)
411     return false;
412   TVectorImage appImg;
413   TStroke *appStroke = new TStroke(*stroke);
414   appImg.addStroke(appStroke);
415   appImg.findRegions();
416 
417   if (regionsReference.size() <= 0) return false;
418 
419   double pbx, pby;
420   double totalArea = 0;
421   pbx = pby = 0.0;
422 
423   if (!regionsWork.isEmpty()) regionsWork.clear();
424 
425   int i, j, index = 0;
426 
427   for (i = 0; i < (int)imgToApply->getRegionCount(); i++) {
428     TRegion *currentRegion = imgToApply->getRegion(i);
429     for (j = 0; j < (int)appImg.getRegionCount(); j++) {
430       TRegion *region = appImg.getRegion(j);
431       if (contains(region, currentRegion)) {
432         scanRegion(currentRegion, index, regionsWork, region->getBBox());
433         index++;
434         int k, subRegionCount = currentRegion->getSubregionCount();
435         for (k = 0; k < subRegionCount; k++) {
436           TRegion *subRegion = currentRegion->getSubregion(k);
437           if (contains(region, subRegion))
438             scanSubRegion(subRegion, index, regionsWork, region->getBBox());
439         }
440       }
441     }
442   }
443 
444   if (regionsWork.size() <= 0) return false;
445 
446   QMap<int, Region>::Iterator it;
447   for (it = regionsWork.begin(); it != regionsWork.end(); it++) {
448     pbx += it.value().m_barycentre.x;
449     pby += it.value().m_barycentre.y;
450     totalArea += it.value().m_area;
451   }
452 
453   workB = TPointD(pbx / totalArea, pby / totalArea);
454 
455   std::vector<MatchingProbs> probVector;
456 
457   RegionDataList::Iterator refIt, workIt;
458   for (refIt = regionsReference.begin(); refIt != regionsReference.end();
459        refIt++)
460     for (workIt = regionsWork.begin(); workIt != regionsWork.end(); workIt++)
461       assignProbs(probVector, refIt.value(), workIt.value(), refIt.key(),
462                   workIt.key());
463 
464   bool filledRegions = false;
465   for (refIt = regionsReference.begin(); refIt != regionsReference.end();
466        refIt++) {
467     int to = 0, from = 0;
468     int valore = 0;
469     do
470       valore = match(probVector, from, to);
471     while ((regionsWork[to].m_match != -1 ||
472             regionsReference[from].m_match != -1) &&
473            valore > 0);
474     if (valore > AMB_TRESH) {
475       regionsWork[to].m_match        = from;
476       regionsReference[from].m_match = to;
477       regionsWork[to].m_styleId      = regionsReference[from].m_styleId;
478       TRegion *reg                   = regionsWork[to].m_region;
479       if (reg && (!selective || (selective && reg->getStyle() == 0))) {
480         reg->setStyle(regionsWork[to].m_styleId);
481         filledRegions = true;
482       }
483     }
484   }
485   return filledRegions;
486 }
487