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}