• 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 {CropAngle} from './CropType';
20
21export abstract class MathUtils {
22    private static readonly EQUALITY_THRESHOLD = 0.0001;
23
24    static roundRect(rect: RectF) {
25        let copy = { ...rect };
26        rect.set(Math.round(copy.left), Math.round(copy.top), Math.round(copy.right), Math.round(copy.bottom));
27    }
28
29    static normalizeRect(rect: RectF, width: number, height: number) {
30        rect.left /= width;
31        rect.right /= width;
32        rect.top /= height;
33        rect.bottom /= height;
34    }
35
36    static revertRect(rect: RectF, width: number, height: number) {
37        rect.left *= width;
38        rect.right *= width;
39        rect.top *= height;
40        rect.bottom *= height;
41    }
42
43    static rectToPoints(rect: RectF): Array<Point> {
44        let points = [];
45        points.push(new Point(rect.left, rect.top));
46        points.push(new Point(rect.right, rect.top));
47        points.push(new Point(rect.right, rect.bottom));
48        points.push(new Point(rect.left, rect.bottom));
49        return points;
50    }
51
52    static swapWidthHeight(rect: RectF) {
53        let centerX = rect.getCenterX();
54        let centerY = rect.getCenterY();
55        let halfWidth = rect.getWidth() / 2;
56        let halfHeight = rect.getHeight() / 2;
57        rect.left = centerX - halfHeight;
58        rect.right = centerX + halfHeight;
59        rect.top = centerY - halfWidth;
60        rect.bottom = centerY + halfWidth;
61    }
62
63    static rotatePoints(inputs: Array<Point>, angle: number, origin: Point): Array<Point> {
64        let alpha = MathUtils.formulaAngle(-angle);
65        let outputs = [];
66        for (let input of inputs) {
67            let dx = input.x - origin.x;
68            let dy = input.y - origin.y;
69            let output = new Point(origin.x, origin.y);
70            output.x += Math.cos(alpha) * dx + Math.sin(alpha) * dy;
71            output.y += -Math.sin(alpha) * dx + Math.cos(alpha) * dy;
72            outputs.push(output);
73        }
74        return outputs;
75    }
76
77    static computeMaxRectWithinLimit(rect: RectF, limit: RectF, rate: number) {
78        let limitWidth = limit.getWidth();
79        let limitHeight = limit.getHeight();
80        let width = limitWidth;
81        let height = limitHeight;
82        if (rate > (limitWidth / limitHeight)) {
83            height = width / rate;
84            rect.left = limit.left;
85            rect.top = limit.top + (limitHeight - height) / 2;
86        } else {
87            width = height * rate;
88            rect.left = limit.left + (limitWidth - width) / 2;
89            rect.top = limit.top;
90        }
91        rect.right = rect.left + width;
92        rect.bottom = rect.top + height;
93    }
94
95    static scaleRectBasedOnPoint(rect: RectF, p: Point, scale: number) {
96        let operate = { ...rect };
97        operate.left = (rect.left - p.x) * scale + p.x;
98        operate.right = (rect.right - p.x) * scale + p.x;
99        operate.top = (rect.top - p.y) * scale + p.y;
100        operate.bottom = (rect.bottom - p.y) * scale + p.y;
101        rect.set(operate.left, operate.top, operate.right, operate.bottom);
102    }
103
104    static hasIntersection(line1: LineSegment, line2: LineSegment): boolean {
105        let p1 = line1.start;
106        let p2 = line1.end;
107        let p3 = line2.start;
108        let p4 = line2.end;
109        if (Math.max(p1.x, p2.x) < Math.min(p3.x, p4.x) || Math.max(p1.y, p2.y) < Math.min(p3.y, p4.y)
110        || Math.max(p3.x, p4.x) < Math.min(p1.x, p2.x) || Math.max(p3.y, p4.y) < Math.min(p1.y, p2.y)) {
111            return false;
112        }
113
114        if ((((p1.x - p3.x) * (p4.y - p3.y) - (p1.y - p3.y) * (p4.x - p3.x))
115        * ((p2.x - p3.x) * (p4.y - p3.y) - (p2.y - p3.y) * (p4.x - p3.x))) >= 0
116        || (((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x))
117        * ((p4.x - p1.x) * (p2.y - p1.y) - (p4.y - p1.y) * (p2.x - p1.x))) >= 0) {
118            return false;
119        }
120        return true;
121    }
122
123    static getIntersection(line1: LineSegment, line2: LineSegment): Point {
124        let A1 = line1.start.y - line1.end.y;
125        let B1 = line1.end.x - line1.start.x;
126        let C1 = A1 * line1.start.x + B1 * line1.start.y;
127
128        let A2 = line2.start.y - line2.end.y;
129        let B2 = line2.end.x - line2.start.x;
130        let C2 = A2 * line2.start.x + B2 * line2.start.y;
131
132        let k = A1 * B2 - A2 * B1;
133        if (Math.abs(k) < MathUtils.EQUALITY_THRESHOLD) {
134            return undefined;
135        }
136
137        let a = B2 / k;
138        let b = -B1 / k;
139        let c = -A2 / k;
140        let d = A1 / k;
141
142        let x = a * C1 + b * C2;
143        let y = c * C1 + d * C2;
144        return new Point(x, y);
145    }
146
147    static findSuitableScale(points: Array<Point>, rect: RectF, origin: Point): number {
148        let scale = 1;
149        let temp = 1;
150        for (let point of points) {
151            if (point.x < rect.left) {
152                temp = (origin.x - point.x) / (origin.x - rect.left);
153                scale = Math.max(scale, temp);
154            }
155            if (point.x > rect.right) {
156                temp = (point.x - origin.x) / (rect.right - origin.x);
157                scale = Math.max(scale, temp);
158            }
159            if (point.y < rect.top) {
160                temp = (origin.y - point.y) / (origin.y - rect.top);
161                scale = Math.max(scale, temp);
162            }
163            if (point.y > rect.bottom) {
164                temp = (point.y - origin.y) / (rect.bottom - origin.y);
165                scale = Math.max(scale, temp);
166            }
167        }
168        return scale;
169    }
170
171    static fixImageMove(rotated: Array<Point>, flipImage: RectF): Array<number> {
172        let offsetX = 0;
173        let offsetY = 0;
174        for (let point of rotated) {
175            if (point.x < flipImage.left) {
176                offsetX = Math.min(offsetX, point.x - flipImage.left);
177            } else if (point.x > flipImage.right) {
178                offsetX = Math.max(offsetX, point.x - flipImage.right);
179            }
180            if (point.y < flipImage.top) {
181                offsetY = Math.min(offsetY, point.y - flipImage.top);
182            } else if (point.y > flipImage.bottom) {
183                offsetY = Math.max(offsetY, point.y - flipImage.bottom);
184            }
185        }
186        return [offsetX, offsetY];
187    }
188
189    static isOddRotation(angle: number): boolean {
190        if (angle == -CropAngle.ONE_QUARTER_CIRCLE_ANGLE || angle == -CropAngle.THREE_QUARTER_CIRCLE_ANGLE) {
191            return true;
192        }
193        return false;
194    }
195
196    static limitCornerIfLineIntersect(outerLine, diagonal, rect) {
197        let origin = new Point(rect.getCenterX(), rect.getCenterY());
198        if (MathUtils.hasIntersection(outerLine, diagonal)) {
199            let intersection = MathUtils.getIntersection(outerLine, diagonal);
200            if (intersection == undefined) {
201                return;
202            }
203            if (intersection.x < origin.x) {
204                rect.left = intersection.x;
205            } else {
206                rect.right = intersection.x;
207            }
208            if (intersection.y < origin.y) {
209                rect.top = intersection.y;
210            } else {
211                rect.bottom = intersection.y;
212            }
213        }
214    }
215
216    static limitRectInRotated(rect: RectF, outerLines: Array<LineSegment>) {
217        let copy = new RectF();
218        copy.set(rect.left, rect.top, rect.right, rect.bottom);
219        let diagonal1 = new LineSegment(new Point(copy.left, copy.top), new Point(copy.right, copy.bottom));
220        for (let line of outerLines) {
221            MathUtils.limitCornerIfLineIntersect(line, diagonal1, copy);
222        }
223
224        let diagonal2 = new LineSegment(new Point(copy.left, copy.bottom), new Point(copy.right, copy.top));
225        for (let line of outerLines) {
226            MathUtils.limitCornerIfLineIntersect(line, diagonal2, copy);
227        }
228        rect.set(copy.left, copy.top, copy.right, copy.bottom);
229    }
230
231    static limitRectInRotatedBasedOnPoint(baseIndex: number, rect: RectF, rotatedLines: Array<LineSegment>) {
232        let points = MathUtils.rectToPoints(rect);
233        let base = points[baseIndex];
234        points.splice(baseIndex, 1);
235        let scale = 1;
236        for (let point of points) {
237            let line = new LineSegment(base, point);
238            for (let rotatedLine of rotatedLines) {
239                if (MathUtils.hasIntersection(line, rotatedLine)) {
240                    let p = MathUtils.getIntersection(line, rotatedLine);
241                    if (p == undefined) {
242                        continue;
243                    }
244                    let tempScale
245                        = Math.hypot(p.x - base.x, p.y - base.y) / Math.hypot(point.x - base.x, point.y - base.y);
246                    scale = Math.min(scale, (tempScale > MathUtils.EQUALITY_THRESHOLD) ? tempScale : 1);
247                }
248            }
249        }
250        MathUtils.scaleRectBasedOnPoint(rect, base, scale);
251    }
252
253    static getMaxFixedRectSize(rate: number, maxWidth: number, maxHeight: number): Array<number> {
254        let width = 0;
255        let height = 0;
256        if (rate > (maxWidth / maxHeight)) {
257            width = maxWidth;
258            height = width / rate;
259        } else {
260            height = maxHeight;
261            width = height * rate;
262        }
263        return [width, height];
264    }
265
266    static getMinFixedRectSize(rate: number, minLength: number): Array<number> {
267        let width = minLength;
268        let height = minLength;
269        if (rate > 1) {
270            width = height * rate;
271        } else {
272            height = width / rate;
273        }
274        return [width, height];
275    }
276
277    static areRectSame(rect1: RectF, rect2: RectF): boolean {
278        if (rect1.left == rect2.left && rect1.top == rect2.top
279        && rect1.right == rect2.right && rect1.bottom == rect2.bottom) {
280            return true;
281        }
282        return false;
283    }
284
285    static formulaAngle(angle: number): number {
286        return angle * Math.PI / CropAngle.HALF_CIRCLE_ANGLE;
287    }
288}