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