• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { LineSegment } from '../base/Line';
17import { Point } from '../base/Point';
18import { RectF } from '../base/Rect';
19import { Ratio } from '../base/Ratio';
20import { Log } from '@ohos/base/src/main/ets/utils/Log';
21import { CropRatioType, CropAngle } from './CropType';
22import { MathUtils } from './MathUtils';
23import  screenManager  from '@ohos/base/src/main/ets/manager/ScreenManager';
24
25export class CropShow {
26    private TAG: string = 'CropShow';
27    private static readonly DEFAULT_MIN_SIDE_LENGTH: number = 90;
28    private static readonly DEFAULT_TOUCH_BOUND: number = 20;
29    private static readonly BASE_SCALE_VALUE: number = 1.0;
30    private limitRect: RectF = undefined;
31    private cropRect: RectF = undefined;
32    private imageRect: RectF = undefined;
33    private ratio: Ratio = undefined;
34    private screenMaxSide: number = 0;
35    private screenMinSide: number = 0;
36    private minSideLength: number = CropShow.DEFAULT_MIN_SIDE_LENGTH;
37    private touchBound: number = CropShow.DEFAULT_TOUCH_BOUND;
38    private rotationAngle: number = 0;
39    private horizontalAngle: number = 0;
40    private maxScaleFactorW: number = CropShow.BASE_SCALE_VALUE;
41    private maxScaleFactorH: number = CropShow.BASE_SCALE_VALUE;
42    private isFlipHorizontal: boolean = false;
43    private isFlipVertically: boolean = false;
44    private isLeft: boolean = false;
45    private isRight: boolean = false;
46    private isTop: boolean = false;
47    private isBottom: boolean = false;
48    private isHorizontalSide: boolean = false;
49    private isVerticalSide: boolean = false;
50
51    constructor() {
52        this.limitRect = new RectF();
53        this.imageRect = new RectF();
54        this.cropRect = new RectF();
55        this.ratio = new Ratio(-1, -1);
56
57        let screenWidth = Math.ceil(screenManager.getWinWidth());
58        let screenHeight = Math.ceil(screenManager.getWinHeight());
59        this.screenMaxSide = Math.max(screenWidth, screenHeight);
60        this.screenMinSide = Math.min(screenWidth, screenHeight);
61    }
62
63    init(limit: RectF, imageRatio: number) {
64        this.limitRect.set(limit.left, limit.top, limit.right, limit.bottom);
65        MathUtils.computeMaxRectWithinLimit(this.imageRect, limit, imageRatio);
66        this.cropRect.set(this.imageRect.left, this.imageRect.top, this.imageRect.right, this.imageRect.bottom);
67        this.ratio.set(-1, -1);
68        this.rotationAngle = 0;
69        this.horizontalAngle = 0;
70        this.isFlipHorizontal = false;
71        this.isFlipVertically = false;
72    }
73
74    syncLimitRect(limit: RectF) {
75        this.limitRect.set(limit.left, limit.top, limit.right, limit.bottom);
76        this.enlargeCropArea();
77    }
78
79    getCropRect(): RectF {
80        let crop = new RectF();
81        crop.set(this.cropRect.left, this.cropRect.top, this.cropRect.right, this.cropRect.bottom);
82        return crop;
83    }
84
85    getImageRect(): RectF {
86        let image = new RectF();
87        image.set(this.imageRect.left, this.imageRect.top, this.imageRect.right, this.imageRect.bottom);
88        return image;
89    }
90
91    setImageRect(image: RectF) {
92        this.imageRect.set(image.left, image.top, image.right, image.bottom);
93    }
94
95    syncRotationAngle(angle: number) {
96        this.rotationAngle = angle;
97        MathUtils.swapWidthHeight(this.cropRect);
98        this.swapCurrentRatio();
99        this.enlargeCropArea();
100    }
101
102    private swapCurrentRatio() {
103        let W = this.ratio.getW();
104        let H = this.ratio.getH();
105        this.ratio.set(H, W);
106    }
107
108    private getDisplayCenter(): Point {
109        return new Point(this.limitRect.getCenterX(), this.limitRect.getCenterY());
110    }
111
112    syncHorizontalAngle(angle: number) {
113        this.horizontalAngle = angle;
114
115        let points = MathUtils.rectToPoints(this.cropRect);
116        let origin = this.getDisplayCenter();
117        let totalAngle = -(this.rotationAngle + this.horizontalAngle);
118        let rotated = MathUtils.rotatePoints(points, totalAngle, origin);
119        let scale = MathUtils.findSuitableScale(rotated, this.imageRect, origin);
120        MathUtils.scaleRectBasedOnPoint(this.imageRect, origin, scale);
121    }
122
123    setFlip(isFlipHorizontal: boolean, isFlipVertically: boolean) {
124        this.isFlipHorizontal = isFlipHorizontal;
125        this.isFlipVertically = isFlipVertically;
126    }
127
128    setRatio(ratio: CropRatioType) {
129        switch (ratio) {
130            case CropRatioType.RATIO_TYPE_FREE:
131                this.ratio.set(-1, -1);
132                break;
133            case CropRatioType.RATIO_TYPE_HORIZONTAL:
134                this.ratio.set(this.screenMaxSide, this.screenMinSide);
135                break;
136            case CropRatioType.RATIO_TYPE_VERTICAL:
137                this.ratio.set(this.screenMinSide, this.screenMaxSide);
138                break;
139            case CropRatioType.RATIO_TYPE_1_1:
140                this.ratio.set(1, 1);
141                break;
142            case CropRatioType.RATIO_TYPE_16_9:
143                this.ratio.set(16, 9);
144                break;
145            case CropRatioType.RATIO_TYPE_9_16:
146                this.ratio.set(9, 16);
147                break;
148            case CropRatioType.RATIO_TYPE_4_3:
149                this.ratio.set(4, 3);
150                break;
151            case CropRatioType.RATIO_TYPE_3_4:
152                this.ratio.set(3, 4);
153                break;
154            case CropRatioType.RATIO_TYPE_3_2:
155                this.ratio.set(3, 2);
156                break;
157            case CropRatioType.RATIO_TYPE_2_3:
158                this.ratio.set(2, 3);
159                break;
160            default:
161               Log.warn(this.TAG, 'setRatio: unknown ratio');
162                break;
163        }
164        if (this.ratio.isValid()) {
165            MathUtils.computeMaxRectWithinLimit(this.cropRect, this.limitRect, this.ratio.getRate());
166            let imageLines = this.getCurrentImageLines();
167            MathUtils.limitRectInRotated(this.cropRect, imageLines);
168            this.imageCropCompare();
169            this.enlargeCropArea();
170        }
171    }
172
173    setMaxScaleFactor(factorW: number, factorH: number) {
174        this.maxScaleFactorW = factorW;
175        this.maxScaleFactorH = factorH;
176    }
177
178    couldEnlargeImage(): boolean {
179        return (this.couldEnlargeImageW() && this.couldEnlargeImageH());
180    }
181
182    private couldEnlargeImageW(): boolean {
183        let scaleFactorW = this.imageRect.getWidth() / this.cropRect.getWidth();
184        return (scaleFactorW >= this.maxScaleFactorW ? false : true);
185    }
186
187    private couldEnlargeImageH(): boolean {
188        let scaleFactorH = this.imageRect.getHeight() / this.cropRect.getHeight();
189        return (scaleFactorH >= this.maxScaleFactorH ? false : true);
190    }
191
192    enlargeCropArea() {
193        let newCrop = new RectF();
194        let cropRatio = this.cropRect.getWidth() / this.cropRect.getHeight();
195        MathUtils.computeMaxRectWithinLimit(newCrop, this.limitRect, cropRatio);
196        let scale = newCrop.getWidth() / this.cropRect.getWidth();
197
198        let tX = this.isFlipHorizontal ? -1 : 1;
199        let tY = this.isFlipVertically ? -1 : 1;
200        let origin = this.getDisplayCenter();
201        let preCenterX = this.cropRect.getCenterX() * tX + (this.isFlipHorizontal ? 2 * origin.x : 0);
202        let preCenterY = this.cropRect.getCenterY() * tY + (this.isFlipVertically ? 2 * origin.y : 0);
203        let preCenter = new Point(preCenterX, preCenterY);
204        let angle = this.rotationAngle * tX * tY + this.horizontalAngle;
205        let rotated = MathUtils.rotatePoints([preCenter], -angle, origin);
206
207        MathUtils.scaleRectBasedOnPoint(this.imageRect, rotated[0], scale);
208
209        let offsetX = newCrop.getCenterX() - preCenter.x;
210        let offsetY = newCrop.getCenterY() - preCenter.y;
211        let alpha = MathUtils.formulaAngle(angle);
212        let x = Math.cos(alpha) * offsetX + Math.sin(alpha) * offsetY;
213        let y = -Math.sin(alpha) * offsetX + Math.cos(alpha) * offsetY;
214        this.imageRect.move(x, y);
215
216        this.cropRect.set(newCrop.left, newCrop.top, newCrop.right, newCrop.bottom);
217    }
218
219    imageCropCompare(): void {
220        let imageRect = this.getImageRect();
221        let cropRect = this.getCropRect();
222        let imageRectWidth = imageRect.getWidth();
223        let imageRectHeight = imageRect.getHeight();
224        let cropRectWidth = cropRect.getWidth();
225        let cropRectHeight = cropRect.getHeight();
226        if (imageRectWidth < cropRectWidth) {
227            let scaleRatio = cropRectWidth / imageRectWidth;
228            this.imageRect.scale(scaleRatio);
229        }
230        if (imageRectHeight < cropRectHeight) {
231            let scaleRatio = cropRectHeight / imageRectHeight;
232            this.imageRect.scale(scaleRatio);
233        }
234
235    }
236
237    isCropRectTouch(x: number, y: number): boolean {
238        let w = this.touchBound;
239        let h = this.touchBound;
240        let crop = { ...this.cropRect };
241        let outer = new RectF();
242        outer.set(crop.left - w, crop.top - h, crop.right + w, crop.bottom + h);
243        let inner = new RectF();
244        inner.set(crop.left + w, crop.top + h, crop.right - w, crop.bottom - h);
245        if (outer.isInRect(x, y) && !inner.isInRect(x, y)) {
246            if (x <= inner.left) {
247                this.isLeft = true;
248            } else if (x >= inner.right) {
249                this.isRight = true;
250            }
251
252            if (y <= inner.top) {
253                this.isTop = true;
254            } else if (y >= inner.bottom) {
255                this.isBottom = true;
256            }
257
258            // convert side to conner, when fixed crop ratio
259            if (this.ratio.isValid()) {
260                this.fixSideToConner(x, y);
261            }
262            Log.debug(this.TAG, `isCropTouch: l[${this.isLeft}] r[${this.isRight}] t[${this.isTop}] b[${this.isBottom}]`);
263        }
264        return this.isLeft || this.isRight || this.isTop || this.isBottom;
265    }
266
267    private fixSideToConner(x: number, y: number) {
268        if ((this.isLeft || this.isRight) && !this.isTop && !this.isBottom) {
269            if (y < this.cropRect.getCenterY()) {
270                this.isTop = true;
271            } else {
272                this.isBottom = true;
273            }
274            this.isVerticalSide = true;
275        } else if ((this.isTop || this.isBottom) && !this.isLeft && !this.isRight) {
276            if (x < this.cropRect.getCenterX()) {
277                this.isLeft = true;
278            } else {
279                this.isRight = true;
280            }
281            this.isHorizontalSide = true;
282        }
283    }
284
285    getCurrentFlipImage(): RectF {
286        let center = this.getDisplayCenter();
287        let image = { ...this.imageRect };
288        let flipImage = new RectF();
289        flipImage.left = this.isFlipHorizontal ? (2 * center.x - image.right) : image.left;
290        flipImage.top = this.isFlipVertically ? (2 * center.y - image.bottom) : image.top;
291        flipImage.right = this.isFlipHorizontal ? (2 * center.x - image.left) : image.right;
292        flipImage.bottom = this.isFlipVertically ? (2 * center.y - image.top) : image.bottom;
293        return flipImage;
294    }
295
296    private getCurrentRotatedImage(): RectF {
297        let flipImage = this.getCurrentFlipImage();
298        let points = MathUtils.rectToPoints(flipImage);
299        let origin = this.getDisplayCenter();
300        let rotated = MathUtils.rotatePoints(points, this.rotationAngle, origin);
301        let i = Math.abs(this.rotationAngle / CropAngle.ONE_QUARTER_CIRCLE_ANGLE);
302        let j = (i + 2) % rotated.length;
303        let image = new RectF();
304        image.set(rotated[i].x, rotated[i].y, rotated[j].x, rotated[j].y);
305        return image;
306    }
307
308    private getCurrentImageLines(): Array<LineSegment> {
309        let flipImage = this.getCurrentFlipImage();
310        let imagePoints = MathUtils.rectToPoints(flipImage);
311        let origin = this.getDisplayCenter();
312        let tX = this.isFlipHorizontal ? -1 : 1;
313        let tY = this.isFlipVertically ? -1 : 1;
314        let angle = this.rotationAngle * tX * tY + this.horizontalAngle;
315        let rotated = MathUtils.rotatePoints(imagePoints, angle, origin);
316
317        let imageLines = [];
318        for (let i = 0; i < rotated.length; i++) {
319            let j = (i + 1) % rotated.length;
320            imageLines.push(
321                new LineSegment(new Point(rotated[i].x, rotated[i].y), new Point(rotated[j].x, rotated[j].y)));
322        }
323        return imageLines;
324    }
325
326    moveCropRect(offsetX: number, offsetY: number) {
327        // crop rect in fixed mode
328        if (this.ratio.isValid()) {
329            this.moveInFixedMode(offsetX, offsetY);
330        } else {
331            this.moveInFreeMode(offsetX, offsetY);
332        }
333    }
334
335    private moveInFixedMode(offsetX: number, offsetY: number) {
336        let x = offsetX;
337        let y = offsetY;
338        if (this.isHorizontalSide) {
339            x = 0;
340        } else if (this.isVerticalSide) {
341            y = 0;
342        }
343        let offsetHypot = Math.hypot(x, y);
344
345        if (this.isLeft && this.isTop) {
346            // left top conner move
347            let isEnlarge = offsetX < 0 || offsetY < 0;
348            if (isEnlarge || this.couldEnlargeImage()) {
349                this.fixLeftTopInFixedMode(offsetHypot, isEnlarge);
350            }
351        } else if (this.isLeft && this.isBottom) {
352            // left bottom conner move
353            let isEnlarge = offsetX < 0 || offsetY > 0;
354            if (isEnlarge || this.couldEnlargeImage()) {
355                this.fixLeftBottomInFixedMode(offsetHypot, isEnlarge);
356            }
357        } else if (this.isRight && this.isTop) {
358            // right top conner move
359            let isEnlarge = offsetX > 0 || offsetY < 0;
360            if (isEnlarge || this.couldEnlargeImage()) {
361                this.fixRightTopInFixedMode(offsetHypot, isEnlarge);
362            }
363        } else if (this.isRight && this.isBottom) {
364            // right bottom conner move
365            let isEnlarge = offsetX > 0 || offsetY > 0;
366            if (isEnlarge || this.couldEnlargeImage()) {
367                this.fixRightBottomInFixedMode(offsetHypot, isEnlarge);
368            }
369        }
370    }
371
372    private fixLeftTopInFixedMode(offsetHypot: number, isEnlarge: boolean) {
373        let crop = this.getCropRect();
374        let rate = this.ratio.getRate();
375        let rect = new RectF();
376        if (isEnlarge) {
377            let limit = { ...this.limitRect };
378            let size = MathUtils.getMaxFixedRectSize(rate, crop.right - limit.left, crop.bottom - limit.top);
379            rect.set(crop.right - size[0], crop.bottom - size[1], crop.right, crop.bottom);
380            let imageLines = this.getCurrentImageLines();
381            MathUtils.limitRectInRotatedBasedOnPoint(2, rect, imageLines);
382        } else {
383            let size = MathUtils.getMinFixedRectSize(rate, this.minSideLength);
384            rect.set(crop.right - size[0], crop.bottom - size[1], crop.right, crop.bottom);
385        }
386        let rectHypot = Math.hypot(rect.getWidth(), rect.getHeight());
387        let cropHypot = Math.hypot(crop.getWidth(), crop.getHeight());
388        let limitHypot = (rectHypot - cropHypot) * (isEnlarge ? 1 : -1);
389        let finalOffsetHypot = Math.min(offsetHypot, Math.max(limitHypot, 0));
390        let tX = isEnlarge ? -1 : 1;
391        let tY = isEnlarge ? -1 : 1;
392        let ratioHypot = Math.hypot(this.ratio.getW(), this.ratio.getH());
393        this.cropRect.left += finalOffsetHypot * tX * this.ratio.getW() / ratioHypot;
394        this.cropRect.top += finalOffsetHypot * tY * this.ratio.getH() / ratioHypot;
395    }
396
397    private fixLeftBottomInFixedMode(offsetHypot: number, isEnlarge: boolean) {
398        let crop = this.getCropRect();
399        let rate = this.ratio.getRate();
400        let rect = new RectF();
401        if (isEnlarge) {
402            let limit = { ...this.limitRect };
403            let size = MathUtils.getMaxFixedRectSize(rate, crop.right - limit.left, limit.bottom - crop.top);
404            rect.set(crop.right - size[0], crop.top, crop.right, crop.top + size[1]);
405            let imageLines = this.getCurrentImageLines();
406            MathUtils.limitRectInRotatedBasedOnPoint(1, rect, imageLines);
407        } else {
408            let size = MathUtils.getMinFixedRectSize(rate, this.minSideLength);
409            rect.set(crop.right - size[0], crop.top, crop.right, crop.top + size[1]);
410        }
411        let rectHypot = Math.hypot(rect.getWidth(), rect.getHeight());
412        let cropHypot = Math.hypot(crop.getWidth(), crop.getHeight());
413        let limitHypot = (rectHypot - cropHypot) * (isEnlarge ? 1 : -1);
414        let finalOffsetHypot = Math.min(offsetHypot, Math.max(limitHypot, 0));
415        let tX = isEnlarge ? -1 : 1;
416        let tY = isEnlarge ? 1 : -1;
417        let ratioHypot = Math.hypot(this.ratio.getW(), this.ratio.getH());
418        this.cropRect.left += finalOffsetHypot * tX * this.ratio.getW() / ratioHypot;
419        this.cropRect.bottom += finalOffsetHypot * tY * this.ratio.getH() / ratioHypot;
420    }
421
422    private fixRightTopInFixedMode(offsetHypot: number, isEnlarge: boolean) {
423        let crop = this.getCropRect();
424        let rate = this.ratio.getRate();
425        let rect = new RectF();
426        if (isEnlarge) {
427            let limit = { ...this.limitRect };
428            let size = MathUtils.getMaxFixedRectSize(rate, limit.right - crop.left, crop.bottom - limit.top);
429            rect.set(crop.left, crop.bottom - size[1], crop.left + size[0], crop.bottom);
430            let imageLines = this.getCurrentImageLines();
431            MathUtils.limitRectInRotatedBasedOnPoint(3, rect, imageLines);
432        } else {
433            let size = MathUtils.getMinFixedRectSize(rate, this.minSideLength);
434            rect.set(crop.left, crop.bottom - size[1], crop.left + size[0], crop.bottom);
435        }
436        let rectHypot = Math.hypot(rect.getWidth(), rect.getHeight());
437        let cropHypot = Math.hypot(crop.getWidth(), crop.getHeight());
438        let limitHypot = (rectHypot - cropHypot) * (isEnlarge ? 1 : -1);
439        let finalOffsetHypot = Math.min(offsetHypot, Math.max(limitHypot, 0));
440        let tX = isEnlarge ? 1 : -1;
441        let tY = isEnlarge ? -1 : 1;
442        let ratioHypot = Math.hypot(this.ratio.getW(), this.ratio.getH());
443        this.cropRect.right += finalOffsetHypot * tX * this.ratio.getW() / ratioHypot;
444        this.cropRect.top += finalOffsetHypot * tY * this.ratio.getH() / ratioHypot;
445    }
446
447    private fixRightBottomInFixedMode(offsetHypot: number, isEnlarge: boolean) {
448        let crop = this.getCropRect();
449        let rate = this.ratio.getRate();
450        let rect = new RectF();
451        if (isEnlarge) {
452            let limit = { ...this.limitRect };
453            let size = MathUtils.getMaxFixedRectSize(rate, limit.right - crop.left, limit.bottom - crop.top);
454            rect.set(crop.left, crop.top, crop.left + size[0], crop.top + size[1]);
455            let imageLines = this.getCurrentImageLines();
456            MathUtils.limitRectInRotatedBasedOnPoint(0, rect, imageLines);
457        } else {
458            let size = MathUtils.getMinFixedRectSize(rate, this.minSideLength);
459            rect.set(crop.left, crop.top, crop.left + size[0], crop.top + size[1]);
460        }
461        let rectHypot = Math.hypot(rect.getWidth(), rect.getHeight());
462        let cropHypot = Math.hypot(crop.getWidth(), crop.getHeight());
463        let limitHypot = (rectHypot - cropHypot) * (isEnlarge ? 1 : -1);
464        let finalOffsetHypot = Math.min(offsetHypot, Math.max(limitHypot, 0));
465        let tX = isEnlarge ? 1 : -1;
466        let tY = isEnlarge ? 1 : -1;
467        let ratioHypot = Math.hypot(this.ratio.getW(), this.ratio.getH());
468        this.cropRect.right += finalOffsetHypot * tX * this.ratio.getW() / ratioHypot;
469        this.cropRect.bottom += finalOffsetHypot * tY * this.ratio.getH() / ratioHypot;
470    }
471
472    private moveInFreeMode(offsetX: number, offsetY: number) {
473        let crop = this.getCropRect();
474        let limit = { ...this.limitRect };
475        let image = this.getCurrentRotatedImage();
476        let minLength = this.minSideLength;
477        let imageLines = this.getCurrentImageLines();
478        if (this.isLeft) {
479            if (offsetX < 0 || this.couldEnlargeImageW()) {
480                let left = Math.min(crop.left + offsetX, crop.right - minLength);
481                left = Math.max(left, image.left, limit.left);
482                this.cropRect.left = this.fixLeftInFreeMode(left, crop, imageLines);
483                crop.left = this.cropRect.left;
484            }
485        } else if (this.isRight) {
486            if (offsetX > 0 || this.couldEnlargeImageW()) {
487                let right = Math.max(crop.right + offsetX, crop.left + minLength);
488                right = Math.min(right, image.right, limit.right);
489                this.cropRect.right = this.fixRightInFreeMode(right, crop, imageLines);
490                crop.right = this.cropRect.right;
491            }
492        }
493        if (this.isTop) {
494            if (offsetY < 0 || this.couldEnlargeImageH()) {
495                let top = Math.min(crop.top + offsetY, crop.bottom - minLength);
496                top = Math.max(top, image.top, limit.top);
497                this.cropRect.top = this.fixTopInFreeMode(top, crop, imageLines);
498            }
499        } else if (this.isBottom) {
500            if (offsetY > 0 || this.couldEnlargeImageH()) {
501                let bottom = Math.max(crop.bottom + offsetY, crop.top + minLength);
502                bottom = Math.min(bottom, image.bottom, limit.bottom);
503                this.cropRect.bottom = this.fixBottomInFreeMode(bottom, crop, imageLines);
504            }
505        }
506    }
507
508    private fixLeftInFreeMode(left: number, crop: RectF, imageLines: Array<LineSegment>): number {
509        let leftLine = new LineSegment(new Point(left, crop.top), new Point(left, crop.bottom));
510        let adjacentLines = [];
511        adjacentLines.push(new LineSegment(new Point(left, crop.top), new Point(crop.right, crop.top)));
512        adjacentLines.push(new LineSegment(new Point(left, crop.bottom), new Point(crop.right, crop.bottom)));
513        let fixedLeft = left;
514        for (let imageLine of imageLines) {
515            if (MathUtils.hasIntersection(imageLine, leftLine)) {
516                let result = this.tryToFindFixedSide(adjacentLines, imageLine, left, true, true);
517                fixedLeft = Math.max(fixedLeft, result);
518            }
519        }
520        return fixedLeft;
521    }
522
523    private fixRightInFreeMode(right: number, crop: RectF, imageLines: Array<LineSegment>): number {
524        let rightLine = new LineSegment(new Point(right, crop.top), new Point(right, crop.bottom));
525        let adjacentLines = [];
526        adjacentLines.push(new LineSegment(new Point(crop.left, crop.top), new Point(right, crop.top)));
527        adjacentLines.push(new LineSegment(new Point(crop.left, crop.bottom), new Point(right, crop.bottom)));
528        let fixedRight = right;
529        for (let imageLine of imageLines) {
530            if (MathUtils.hasIntersection(imageLine, rightLine)) {
531                let result = this.tryToFindFixedSide(adjacentLines, imageLine, right, true, false);
532                fixedRight = Math.min(fixedRight, result);
533            }
534        }
535        return fixedRight;
536    }
537
538    private fixTopInFreeMode(top: number, crop: RectF, imageLines: Array<LineSegment>): number {
539        let topLine = new LineSegment(new Point(crop.left, top), new Point(crop.right, top));
540        let adjacentLines = [];
541        adjacentLines.push(new LineSegment(new Point(crop.left, top), new Point(crop.left, crop.bottom)));
542        adjacentLines.push(new LineSegment(new Point(crop.right, top), new Point(crop.right, crop.bottom)));
543        let fixedTop = top;
544        for (let imageLine of imageLines) {
545            if (MathUtils.hasIntersection(imageLine, topLine)) {
546                let result = this.tryToFindFixedSide(adjacentLines, imageLine, top, false, true);
547                fixedTop = Math.max(fixedTop, result);
548            }
549        }
550        return fixedTop;
551    }
552
553    private fixBottomInFreeMode(bottom: number, crop: RectF, imageLines: Array<LineSegment>): number {
554        let bottomLine = new LineSegment(new Point(crop.left, bottom), new Point(crop.right, bottom));
555        let adjacentLines = [];
556        adjacentLines.push(new LineSegment(new Point(crop.left, crop.top), new Point(crop.left, bottom)));
557        adjacentLines.push(new LineSegment(new Point(crop.right, crop.top), new Point(crop.right, bottom)));
558        let fixedBottom = bottom;
559        for (let imageLine of imageLines) {
560            if (MathUtils.hasIntersection(imageLine, bottomLine)) {
561                let result = this.tryToFindFixedSide(adjacentLines, imageLine, bottom, false, false);
562                fixedBottom = Math.min(fixedBottom, result);
563            }
564        }
565        return fixedBottom;
566    }
567
568    private tryToFindFixedSide(adjacentLines: Array<LineSegment>, imageLine: LineSegment,
569                               side: number, isCompareX: boolean, isCompareMax: boolean): number {
570        let fixedSide = side;
571        let compareFunc = isCompareMax ? Math.max : Math.min;
572        for (let adjacentLine of adjacentLines) {
573            if (MathUtils.hasIntersection(imageLine, adjacentLine)) {
574                let intersection = MathUtils.getIntersection(imageLine, adjacentLine);
575                if (intersection == undefined) {
576                    continue;
577                }
578                let compare = isCompareX ? intersection.x : intersection.y;
579                fixedSide = compareFunc(side, compare);
580            }
581        }
582        return fixedSide;
583    }
584
585    endCropRectMove() {
586        this.isLeft = false;
587        this.isRight = false;
588        this.isTop = false;
589        this.isBottom = false;
590        this.isHorizontalSide = false;
591        this.isVerticalSide = false;
592    }
593}