1 /*$
2 Copyright (C) 2013-2020 Azel.
3
4 This file is part of AzPainter.
5
6 AzPainter is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 AzPainter is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 $*/
19
20 /*****************************************
21 * DrawData
22 *
23 * 計算関連
24 *****************************************/
25
26 #include <stdlib.h>
27 #include <math.h>
28
29 #include "mDef.h"
30 #include "mRectBox.h"
31
32 #include "defDraw.h"
33 #include "defConfig.h"
34 #include "defMacros.h"
35
36 #include "draw_calc.h"
37
38 #include "TileImage.h"
39
40
41
42 /** 現在の倍率&角度から、キャンバス用パラメータセット */
43
drawCalc_setCanvasViewParam(DrawData * p)44 void drawCalc_setCanvasViewParam(DrawData *p)
45 {
46 double d;
47
48 d = p->canvas_zoom * 0.001;
49
50 p->viewparam.scale = d;
51 p->viewparam.scalediv = 1.0 / d;
52
53 d = p->canvas_angle * M_MATH_PI / 18000.0;
54
55 p->viewparam.rd = d;
56 p->viewparam.cos = cos(d);
57 p->viewparam.sin = sin(d);
58 p->viewparam.cosrev = p->viewparam.cos;
59 p->viewparam.sinrev = -(p->viewparam.sin);
60 }
61
62 /** 指定位置がキャンバスの範囲内か */
63
drawCalc_isPointInCanvasArea(DrawData * p,mPoint * pt)64 mBool drawCalc_isPointInCanvasArea(DrawData *p,mPoint *pt)
65 {
66 return (pt->x >= 0 && pt->x < p->szCanvas.w && pt->y >= 0 && pt->y < p->szCanvas.h);
67 }
68
69
70 //==============================
71 // 座標変換
72 //==============================
73
74
75 /** 領域座標 (double) -> イメージ位置 (double) */
76
drawCalc_areaToimage_double(DrawData * p,double * dx,double * dy,double x,double y)77 void drawCalc_areaToimage_double(DrawData *p,double *dx,double *dy,double x,double y)
78 {
79 double xx,yy;
80
81 x += p->ptScroll.x - (p->szCanvas.w >> 1);
82 y += p->ptScroll.y - (p->szCanvas.h >> 1);
83
84 xx = x * p->viewparam.cosrev - y * p->viewparam.sinrev;
85 yy = x * p->viewparam.sinrev + y * p->viewparam.cosrev;
86
87 if(p->canvas_mirror) xx = -xx;
88
89 *dx = xx * p->viewparam.scalediv + p->imgoriginX;
90 *dy = yy * p->viewparam.scalediv + p->imgoriginY;
91 }
92
93 /** 領域座標 (mPoint) -> イメージ位置 (mDoublePoint) */
94
drawCalc_areaToimage_double_pt(DrawData * p,mDoublePoint * dst,mPoint * src)95 void drawCalc_areaToimage_double_pt(DrawData *p,mDoublePoint *dst,mPoint *src)
96 {
97 drawCalc_areaToimage_double(p, &dst->x, &dst->y, src->x, src->y);
98 }
99
100 /** 領域座標 (double) -> イメージ位置 (mPoint) */
101
drawCalc_areaToimage_pt(DrawData * p,mPoint * pt,double x,double y)102 void drawCalc_areaToimage_pt(DrawData *p,mPoint *pt,double x,double y)
103 {
104 drawCalc_areaToimage_double(p, &x, &y, x, y);
105
106 pt->x = floor(x);
107 pt->y = floor(y);
108 }
109
110 /** キャンバス座標における相対値をイメージ座標での相対値に変換 */
111
drawCalc_areaToimage_relative(DrawData * p,mPoint * dst,mPoint * src)112 void drawCalc_areaToimage_relative(DrawData *p,mPoint *dst,mPoint *src)
113 {
114 dst->x = (int)((src->x * p->viewparam.cosrev - src->y * p->viewparam.sinrev) * p->viewparam.scalediv);
115 dst->y = (int)((src->x * p->viewparam.sinrev + src->y * p->viewparam.cosrev) * p->viewparam.scalediv);
116
117 if(p->canvas_mirror)
118 dst->x = -(dst->x);
119 }
120
121 /** イメージ位置 (int) -> 領域座標 (mPoint) */
122
drawCalc_imageToarea_pt(DrawData * p,mPoint * dst,int x,int y)123 void drawCalc_imageToarea_pt(DrawData *p,mPoint *dst,int x,int y)
124 {
125 double dx,dy;
126
127 dx = (x - p->imgoriginX) * p->viewparam.scale;
128 dy = (y - p->imgoriginY) * p->viewparam.scale;
129
130 if(p->canvas_mirror) dx = -dx;
131
132 dst->x = round((dx * p->viewparam.cos - dy * p->viewparam.sin) + (p->szCanvas.w >> 1) - p->ptScroll.x);
133 dst->y = round((dx * p->viewparam.sin + dy * p->viewparam.cos) + (p->szCanvas.h >> 1) - p->ptScroll.y);
134 }
135
136 /** イメージ位置 (double) -> 領域座標 (mPoint) */
137
drawCalc_imageToarea_pt_double(DrawData * p,mPoint * dst,double x,double y)138 void drawCalc_imageToarea_pt_double(DrawData *p,mPoint *dst,double x,double y)
139 {
140 double dx,dy;
141
142 dx = (x - p->imgoriginX) * p->viewparam.scale;
143 dy = (y - p->imgoriginY) * p->viewparam.scale;
144
145 if(p->canvas_mirror) dx = -dx;
146
147 dst->x = round((dx * p->viewparam.cos - dy * p->viewparam.sin) + (p->szCanvas.w >> 1) - p->ptScroll.x);
148 dst->y = round((dx * p->viewparam.sin + dy * p->viewparam.cos) + (p->szCanvas.h >> 1) - p->ptScroll.y);
149 }
150
151 /** キャンバス中央におけるイメージ位置取得 */
152
drawCalc_getImagePos_atCanvasCenter(DrawData * p,double * px,double * py)153 void drawCalc_getImagePos_atCanvasCenter(DrawData *p,double *px,double *py)
154 {
155 drawCalc_areaToimage_double(p, px, py, p->szCanvas.w * 0.5, p->szCanvas.h * 0.5);
156 }
157
158 /** イメージ座標 (mBox) -> 領域座標 (mBox)
159 *
160 * @return FALSE で範囲外 */
161
drawCalc_imageToarea_box(DrawData * p,mBox * dst,mBox * src)162 mBool drawCalc_imageToarea_box(DrawData *p,mBox *dst,mBox *src)
163 {
164 mPoint pt[4];
165 int x1,y1,x2,y2,i;
166
167 //右・下端は+1
168
169 x1 = src->x, y1 = src->y;
170 x2 = x1 + src->w;
171 y2 = y1 + src->h;
172
173 //最小・最大値
174
175 if(p->canvas_angle == 0)
176 {
177 drawCalc_imageToarea_pt(p, pt, x1, y1);
178 drawCalc_imageToarea_pt(p, pt + 1, x2, y2);
179
180 if(pt[0].x < pt[1].x)
181 x1 = pt[0].x, x2 = pt[1].x;
182 else
183 x1 = pt[1].x, x2 = pt[0].x;
184
185 if(pt[0].y < pt[1].y)
186 y1 = pt[0].y, y2 = pt[1].y;
187 else
188 y1 = pt[1].y, y2 = pt[0].y;
189 }
190 else
191 {
192 drawCalc_imageToarea_pt(p, pt , x1, y1);
193 drawCalc_imageToarea_pt(p, pt + 1, x2, y1);
194 drawCalc_imageToarea_pt(p, pt + 2, x1, y2);
195 drawCalc_imageToarea_pt(p, pt + 3, x2, y2);
196
197 x1 = x2 = pt[0].x;
198 y1 = y2 = pt[0].y;
199
200 for(i = 1; i < 4; i++)
201 {
202 if(x1 > pt[i].x) x1 = pt[i].x;
203 if(y1 > pt[i].y) y1 = pt[i].y;
204 if(x2 < pt[i].x) x2 = pt[i].x;
205 if(y2 < pt[i].y) y2 = pt[i].y;
206 }
207 }
208
209 //念のため範囲拡張
210
211 x1--, y1--;
212 x2++, y2++;
213
214 //範囲外判定
215
216 if(x2 < 0 || y2 < 0 || x1 >= p->szCanvas.w || y1 >= p->szCanvas.h)
217 return FALSE;
218
219 //調整
220
221 if(x1 < 0) x1 = 0;
222 if(y1 < 0) y1 = 0;
223 if(x2 >= p->szCanvas.w) x2 = p->szCanvas.w - 1;
224 if(y2 >= p->szCanvas.h) y2 = p->szCanvas.h - 1;
225
226 //
227
228 dst->x = x1, dst->y = y1;
229 dst->w = x2 - x1 + 1;
230 dst->h = y2 - y1 + 1;
231
232 return TRUE;
233 }
234
235 /** 領域座標 (mBox) -> イメージ座標 (mBox)
236 *
237 * @return FALSE でイメージの範囲外 */
238
drawCalc_areaToimage_box(DrawData * p,mBox * dst,mBox * src)239 mBool drawCalc_areaToimage_box(DrawData *p,mBox *dst,mBox *src)
240 {
241 mPoint pt[4];
242 mRect rc;
243 int i;
244
245 //四隅をイメージ位置に変換
246
247 drawCalc_areaToimage_pt(p, pt , src->x - 1, src->y - 1);
248 drawCalc_areaToimage_pt(p, pt + 1, src->x + src->w, src->y - 1);
249 drawCalc_areaToimage_pt(p, pt + 2, src->x - 1, src->y + src->h);
250 drawCalc_areaToimage_pt(p, pt + 3, src->x + src->w, src->y + src->h);
251
252 //範囲
253
254 rc.x1 = rc.x2 = pt[0].x;
255 rc.y1 = rc.y2 = pt[0].y;
256
257 for(i = 1; i < 4; i++)
258 {
259 if(pt[i].x < rc.x1) rc.x1 = pt[i].x;
260 if(pt[i].y < rc.y1) rc.y1 = pt[i].y;
261 if(pt[i].x > rc.x2) rc.x2 = pt[i].x;
262 if(pt[i].y > rc.y2) rc.y2 = pt[i].y;
263 }
264
265 //イメージ範囲調整
266
267 return drawCalc_getImageBox_rect(p, dst, &rc);
268 }
269
270
271 //==============================
272 // 範囲
273 //==============================
274
275
276 /** mRect を領域の範囲内にクリッピングして mBox へ
277 *
278 * @param dst 範囲がない場合、w = h = 0 */
279
drawCalc_clipArea_toBox(DrawData * p,mBox * dst,mRect * src)280 mBool drawCalc_clipArea_toBox(DrawData *p,mBox *dst,mRect *src)
281 {
282 int x1,y1,x2,y2;
283
284 if(src->x1 > src->x2 || src->y1 > src->y2
285 || src->x2 < 0 || src->y2 < 0
286 || src->x1 >= p->szCanvas.w || src->y1 >= p->szCanvas.h)
287 {
288 dst->w = dst->h = 0;
289 return FALSE;
290 }
291 else
292 {
293 x1 = (src->x1 < 0)? 0: src->x1;
294 y1 = (src->y1 < 0)? 0: src->y1;
295 x2 = (src->x2 >= p->szCanvas.w)? p->szCanvas.w - 1: src->x2;
296 y2 = (src->y2 >= p->szCanvas.h)? p->szCanvas.h - 1: src->y2;
297
298 dst->x = x1, dst->y = y1;
299 dst->w = x2 - x1 + 1;
300 dst->h = y2 - y1 + 1;
301
302 return TRUE;
303 }
304 }
305
306 /** mRect をイメージ範囲内にクリッピング */
307
drawCalc_clipImageRect(DrawData * p,mRect * rc)308 mBool drawCalc_clipImageRect(DrawData *p,mRect *rc)
309 {
310 int x1,y1,x2,y2;
311
312 if(mRectIsEmpty(rc)) return FALSE;
313
314 x1 = rc->x1, y1 = rc->y1;
315 x2 = rc->x2, y2 = rc->y2;
316
317 //範囲外
318
319 if(x2 < 0 || y2 < 0 || x1 >= p->imgw || y1 >= p->imgh)
320 return FALSE;
321
322 //調整
323
324 if(x1 < 0) x1 = 0;
325 if(y1 < 0) y1 = 0;
326 if(x2 >= p->imgw) x2 = p->imgw - 1;
327 if(y2 >= p->imgh) y2 = p->imgh - 1;
328
329 //セット
330
331 rc->x1 = x1, rc->y1 = y1;
332 rc->x2 = x2, rc->y2 = y2;
333
334 return TRUE;
335 }
336
337 /** mRect をイメージの範囲内に収めて mBox へ
338 *
339 * @return FALSE で範囲外、または範囲が空 */
340
drawCalc_getImageBox_rect(DrawData * p,mBox * dst,mRect * rc)341 mBool drawCalc_getImageBox_rect(DrawData *p,mBox *dst,mRect *rc)
342 {
343 mRect rc2;
344
345 rc2 = *rc;
346
347 if(!drawCalc_clipImageRect(p, &rc2))
348 {
349 dst->w = dst->h = 0;
350 return FALSE;
351 }
352 else
353 {
354 dst->x = rc2.x1;
355 dst->y = rc2.y1;
356 dst->w = rc2.x2 - rc2.x1 + 1;
357 dst->h = rc2.y2 - rc2.y1 + 1;
358
359 return TRUE;
360 }
361 }
362
363 /** mBox (イメージ座標) の範囲を結合
364 *
365 * x < 0 の場合は範囲なしとする。
366 *
367 * @return FALSE で両方共範囲なし */
368
drawCalc_unionImageBox(mBox * dst,mBox * src)369 mBool drawCalc_unionImageBox(mBox *dst,mBox *src)
370 {
371 if(dst->x < 0 && src->x < 0)
372 //両方共範囲なし
373 return FALSE;
374 else if(src->x < 0)
375 //src が範囲なし => dst は変化なし
376 return TRUE;
377 else if(dst->x < 0)
378 {
379 //dst が範囲なし => src をコピー
380
381 *dst = *src;
382 return TRUE;
383 }
384 else
385 {
386 //範囲合成
387
388 mBoxUnion(dst, src);
389 return TRUE;
390 }
391 }
392
393 /** src と、src を mx,my 分相対移動した範囲を合成 */
394
drawCalc_unionRect_relmove(mRect * dst,mRect * src,int mx,int my)395 void drawCalc_unionRect_relmove(mRect *dst,mRect *src,int mx,int my)
396 {
397 *dst = *src;
398 mRectRelMove(dst, mx, my);
399
400 mRectUnion(dst, src);
401 }
402
403
404 //=============================
405 // 描画関連
406 //=============================
407
408
409 /** 直線を45度単位に調整
410 *
411 * @param pt 対象点。結果はここに入る。
412 * @param ptstart 始点 */
413
drawCalc_fitLine45(mPoint * pt,mPoint * ptstart)414 void drawCalc_fitLine45(mPoint *pt,mPoint *ptstart)
415 {
416 int dx,dy,adx,ady,n;
417
418 dx = pt->x - ptstart->x;
419 dy = pt->y - ptstart->y;
420
421 adx = abs(dx);
422 ady = abs(dy);
423
424 n = (adx > ady)? adx: ady;
425
426 if(abs(adx - ady) < n / 2)
427 {
428 //45度線
429
430 if(adx < ady)
431 pt->y = ptstart->y + ((dy < 0)? -adx: adx);
432 else
433 pt->x = ptstart->x + ((dx < 0)? -ady: ady);
434 }
435 else if(adx > ady)
436 //水平
437 pt->y = ptstart->y;
438 else
439 //垂直
440 pt->x = ptstart->x;
441 }
442
443 /** pttmp[0,1] から線の角度をラジアンで取得 (イメージ位置に対する角度。反時計回り) */
444
drawCalc_getLineRadian_forImage(DrawData * p)445 double drawCalc_getLineRadian_forImage(DrawData *p)
446 {
447 double x1,y1,x2,y2;
448
449 drawCalc_areaToimage_double(p, &x1, &y1, p->w.pttmp[0].x, p->w.pttmp[0].y);
450 drawCalc_areaToimage_double(p, &x2, &y2, p->w.pttmp[1].x, p->w.pttmp[1].y);
451
452 return atan2(y2 - y1, x2 - x1);
453 }
454
455 /** イメージ移動時の計算
456 *
457 * @param img 相対移動数を計算するためのイメージ
458 * @param ptret 相対移動数が入る
459 * @return 1px でも移動するか */
460
drawCalc_moveImage_onMotion(DrawData * p,TileImage * img,uint32_t state,mPoint * ptret)461 mBool drawCalc_moveImage_onMotion(DrawData *p,TileImage *img,uint32_t state,mPoint *ptret)
462 {
463 mPoint pt,pt2;
464 double x,y;
465
466 //カーソル移動距離
467
468 x = p->w.dptAreaCur.x - p->w.dptAreaLast.x;
469 y = p->w.dptAreaCur.y - p->w.dptAreaLast.y;
470
471 if(state & M_MODS_CTRL) x = 0;
472 if(state & M_MODS_SHIFT) y = 0;
473
474 //総移動数
475
476 p->w.ptd_tmp[0].x += x;
477 p->w.ptd_tmp[0].y += y;
478
479 //総移動数を キャンバス -> イメージ 座標変換
480
481 pt2.x = (int)p->w.ptd_tmp[0].x;
482 pt2.y = (int)p->w.ptd_tmp[0].y;
483
484 drawCalc_areaToimage_relative(p, &pt, &pt2);
485
486 //移動開始時のオフセット位置を加算して絶対値に
487
488 pt.x += p->w.pttmp[0].x;
489 pt.y += p->w.pttmp[0].y;
490
491 //現在位置からの相対移動数
492
493 TileImage_getOffset(img, &pt2);
494
495 pt.x -= pt2.x;
496 pt.y -= pt2.y;
497
498 if(pt.x == 0 && pt.y == 0)
499 return FALSE;
500 else
501 {
502 //総相対移動数
503 p->w.pttmp[1].x += pt.x;
504 p->w.pttmp[1].y += pt.y;
505
506 *ptret = pt;
507
508 return TRUE;
509 }
510 }
511
512
513 //==============================
514 // ほか
515 //==============================
516
517
518 /** 1段階上げ/下げした表示倍率取得 */
519
drawCalc_getZoom_step(DrawData * p,mBool zoomup)520 int drawCalc_getZoom_step(DrawData *p,mBool zoomup)
521 {
522 int n,step;
523
524 n = p->canvas_zoom / 10;
525
526 if((!zoomup && n <= 100) || (zoomup && n < 100))
527 {
528 //100% 以下
529
530 step = APP_CONF->canvasZoomStep_low;
531 if(step == 0) step = 1;
532
533 if(step < 0)
534 {
535 //元の倍率*指定倍率を増減
536
537 step = -step;
538 n = round(p->canvas_zoom * (step / 100.0));
539
540 if(zoomup)
541 {
542 n += p->canvas_zoom;
543 if(n > 1000) n = 1000;
544 }
545 else
546 n = p->canvas_zoom - n;
547 }
548 else
549 {
550 //段階
551 if(zoomup)
552 {
553 n = 100 - (100 - n - 1) / step * step;
554 if(n > 100) n = 100;
555 }
556 else
557 n = 100 - (100 - n + step) / step * step;
558
559 n *= 10;
560 }
561 }
562 else
563 {
564 //100% 以上
565
566 step = APP_CONF->canvasZoomStep_hi;
567
568 if(zoomup)
569 n = (n + step) / step * step;
570 else
571 {
572 n = (n - 1) / step * step;
573 if(n < 100) n = 100;
574 }
575
576 n *= 10;
577 }
578
579 //
580
581 if(n < CANVAS_ZOOM_MIN)
582 n = CANVAS_ZOOM_MIN;
583 else if(n > CANVAS_ZOOM_MAX)
584 n = CANVAS_ZOOM_MAX;
585
586 return n;
587 }
588
589 /** ウィンドウに収まる表示倍率を取得
590 *
591 * 最大で 100% */
592
drawCalc_getZoom_fitWindow(DrawData * p)593 int drawCalc_getZoom_fitWindow(DrawData *p)
594 {
595 mBox box;
596 int w,h;
597
598 //領域の余白 10px 分を引く
599
600 if(p->szCanvas.w < 20 || p->szCanvas.h < 20)
601 w = h = 21;
602 else
603 {
604 w = p->szCanvas.w - 20;
605 h = p->szCanvas.h - 20;
606 }
607
608 //
609
610 box.x = box.y = 0;
611 box.w = p->imgw, box.h = p->imgh;
612
613 mBoxScaleKeepAspect(&box, w, h, TRUE);
614
615 //倍率
616
617 return (int)((double)box.w / p->imgw * 1000 + 0.5);
618 }
619
620 /** キャンバスのスクロールバーの最大値取得
621 *
622 * 四隅をイメージ座標 -> 領域座標変換し、一番大きい半径の値+余白 */
623
drawCalc_getCanvasScrollMax(DrawData * p,mSize * size)624 void drawCalc_getCanvasScrollMax(DrawData *p,mSize *size)
625 {
626 mPoint pt[4];
627 int i,r,rmax = 0;
628 double x,y;
629
630 //イメージの四隅を領域座標に変換。
631 //キャンバス中央からの距離の最大値を取得。
632
633 mMemzero(pt, sizeof(mPoint) * 4);
634
635 pt[1].x = pt[3].x = p->imgw;
636 pt[2].y = pt[3].y = p->imgh;
637
638 for(i = 0; i < 4; i++)
639 {
640 drawCalc_imageToarea_pt(p, pt + i, pt[i].x, pt[i].y);
641
642 x = pt[i].x - (p->szCanvas.w >> 1);
643 y = pt[i].y - (p->szCanvas.h >> 1);
644
645 r = (int)(sqrt(x * x + y * y) + 0.5);
646 if(rmax < r) rmax = r;
647 }
648
649 //
650
651 size->w = rmax * 2 + p->szCanvas.w;
652 size->h = rmax * 2 + p->szCanvas.h;
653 }
654
655
656 //===============================
657 // defCanvasInfo.h
658 //===============================
659
660
661 /** イメージ座標からキャンバス座標に変換 */
662
CanvasDrawInfo_imageToarea(CanvasDrawInfo * p,double x,double y,double * px,double * py)663 void CanvasDrawInfo_imageToarea(CanvasDrawInfo *p,double x,double y,double *px,double *py)
664 {
665 x = (x - p->originx) * p->param->scale;
666 y = (y - p->originy) * p->param->scale;
667 if(p->mirror) x = -x;
668
669 *px = (x * p->param->cos - y * p->param->sin) - p->scrollx;
670 *py = (x * p->param->sin + y * p->param->cos) - p->scrolly;
671 }
672
673 /** イメージ座標からキャンバス座標に変換 (=> mPoint) */
674
CanvasDrawInfo_imageToarea_pt(CanvasDrawInfo * p,double x,double y,mPoint * pt)675 void CanvasDrawInfo_imageToarea_pt(CanvasDrawInfo *p,double x,double y,mPoint *pt)
676 {
677 x = (x - p->originx) * p->param->scale;
678 y = (y - p->originy) * p->param->scale;
679 if(p->mirror) x = -x;
680
681 pt->x = floor((x * p->param->cos - y * p->param->sin) - p->scrollx);
682 pt->y = floor((x * p->param->sin + y * p->param->cos) - p->scrolly);
683 }
684
685 /** イメージ座標を +1 した時のキャンバス座標の増加幅を取得
686 *
687 * @param dst 0:x-x 1:x-y 2:y-x 3:y-y */
688
CanvasDrawInfo_getImageIncParam(CanvasDrawInfo * p,double * dst)689 void CanvasDrawInfo_getImageIncParam(CanvasDrawInfo *p,double *dst)
690 {
691 dst[0] = p->param->scale * p->param->cos;
692 dst[1] = p->param->scale * p->param->sin;
693 dst[2] = -dst[1];
694 dst[3] = dst[0];
695
696 if(p->mirror)
697 {
698 dst[0] = -dst[0];
699 dst[1] = -dst[1];
700 }
701 }
702
703