Merge branch 'feature/test' of https://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into feature/hj
# Conflicts: # src/hooks/useMode.js
This commit is contained in:
commit
38881ead6a
@ -40,6 +40,7 @@ export default function Roof2() {
|
|||||||
zoomOut,
|
zoomOut,
|
||||||
zoom,
|
zoom,
|
||||||
togglePolygonLine,
|
togglePolygonLine,
|
||||||
|
handleOuterlinesTest,
|
||||||
handleOuterlinesTest2,
|
handleOuterlinesTest2,
|
||||||
applyTemplateB,
|
applyTemplateB,
|
||||||
makeRoofPatternPolygon,
|
makeRoofPatternPolygon,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
|
import { getDirection, getDirectionByPoint } from '@/util/canvas-util'
|
||||||
|
|
||||||
export class QLine extends fabric.Group {
|
export class QLine extends fabric.Group {
|
||||||
line
|
line
|
||||||
@ -47,7 +48,7 @@ export class QLine extends fabric.Group {
|
|||||||
this.y2 = y2
|
this.y2 = y2
|
||||||
this.line = line
|
this.line = line
|
||||||
this.fontSize = option.fontSize
|
this.fontSize = option.fontSize
|
||||||
this.direction = option.direction
|
this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 })
|
||||||
this.parent = option.parent
|
this.parent = option.parent
|
||||||
this.idx = option.idx
|
this.idx = option.idx
|
||||||
|
|
||||||
|
|||||||
@ -201,6 +201,22 @@ export function useMode() {
|
|||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'Enter': {
|
||||||
|
const result = prompt('입력하세요 (a(A패턴),b(B패턴),t(지붕))')
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case 'a':
|
||||||
|
applyTemplateA()
|
||||||
|
break
|
||||||
|
case 'b':
|
||||||
|
applyTemplateB()
|
||||||
|
break
|
||||||
|
case 't':
|
||||||
|
templateMode()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,8 +876,17 @@ export function useMode() {
|
|||||||
/**
|
/**
|
||||||
*벽 지붕 외곽선 생성
|
*벽 지붕 외곽선 생성
|
||||||
*/
|
*/
|
||||||
const handleOuterlinesTest = (polygon, offset = 71) => {
|
const handleOuterlinesTest = (offsetInputX, offsetInputY = 0) => {
|
||||||
var offsetPoints = []
|
const polygon = drawWallPolygon()
|
||||||
|
|
||||||
|
let offsetPoints = []
|
||||||
|
const originalMax = 71
|
||||||
|
const transformedMax = 100
|
||||||
|
|
||||||
|
offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
|
||||||
|
|
||||||
|
const offsetX = (offsetInputX / transformedMax) * originalMax * 2
|
||||||
|
const offsetY = (offsetInputY / transformedMax) * originalMax * 2
|
||||||
|
|
||||||
const sortedIndex = getStartIndex(polygon.lines)
|
const sortedIndex = getStartIndex(polygon.lines)
|
||||||
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
|
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
|
||||||
@ -914,17 +939,14 @@ export function useMode() {
|
|||||||
|
|
||||||
// 오프셋 적용
|
// 오프셋 적용
|
||||||
var offsetPoint = {
|
var offsetPoint = {
|
||||||
x1: current.x + unitNormal.x * offset,
|
x1: current.x + unitNormal.x * offsetX,
|
||||||
y1: current.y + unitNormal.y * offset,
|
y1: current.y + unitNormal.y * offsetY,
|
||||||
}
|
}
|
||||||
|
|
||||||
offsetPoints.push(offsetPoint)
|
offsetPoints.push(offsetPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
const roof = makePolygon(offsetPoints)
|
makePolygon(offsetPoints)
|
||||||
setRoof(roof)
|
|
||||||
|
|
||||||
return roof
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1044,6 +1066,8 @@ export function useMode() {
|
|||||||
} else if (polygon.lines.length === 6) {
|
} else if (polygon.lines.length === 6) {
|
||||||
//6각형
|
//6각형
|
||||||
handleOuterLineTemplateA6Points(polygon)
|
handleOuterLineTemplateA6Points(polygon)
|
||||||
|
} else if (polygon.lines.length === 8) {
|
||||||
|
handleOuterLineTemplateA8Points(polygon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1860,6 +1884,214 @@ export function useMode() {
|
|||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 50, offsetInputY = 20) => {
|
||||||
|
let offsetPoints = []
|
||||||
|
|
||||||
|
const originalMax = 71
|
||||||
|
const transformedMax = 100
|
||||||
|
|
||||||
|
let lines = [] //내각라인
|
||||||
|
let outLines = [] //아웃라인
|
||||||
|
let halfLength = 0 //선길이
|
||||||
|
|
||||||
|
const dashedCenterLineOpt = {
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
property: 'centerLine',
|
||||||
|
strokeDashArray: [5, 5],
|
||||||
|
fontSize: 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
const centerLineOpt = {
|
||||||
|
stroke: 'blue',
|
||||||
|
strokeWidth: 5,
|
||||||
|
property: 'bigHoriCenter',
|
||||||
|
fontSize: 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 폴리곤의 각 변을 선으로 생성
|
||||||
|
for (let i = 0; i < polygon.points.length; i++) {
|
||||||
|
const start = polygon.points[i]
|
||||||
|
const end = polygon.points[(i + 1) % polygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
|
||||||
|
|
||||||
|
const color = i % 2 === 0 ? '#A0D468' : 'skyblue'
|
||||||
|
|
||||||
|
const line = new QLine([start.x, start.y, end.x, end.y], {
|
||||||
|
stroke: color,
|
||||||
|
strokeWidth: 2,
|
||||||
|
property: 'normal',
|
||||||
|
fontSize: 14,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 선을 배열에 추가
|
||||||
|
lines.push(line)
|
||||||
|
canvas.add(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
|
||||||
|
|
||||||
|
const sortedIndex = getStartIndex(polygon.lines)
|
||||||
|
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
|
||||||
|
|
||||||
|
if (tmpArraySorted[0].direction === 'right') {
|
||||||
|
//시계방향
|
||||||
|
tmpArraySorted = tmpArraySorted.reverse() //그럼 배열을 거꾸로 만들어서 무조건 반시계방향으로 배열 보정
|
||||||
|
}
|
||||||
|
|
||||||
|
setSortedArray(tmpArraySorted) //recoil에 넣음
|
||||||
|
|
||||||
|
const points = tmpArraySorted.map((line) => ({
|
||||||
|
x: line.x1,
|
||||||
|
y: line.y1,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 외적을 계산하는 함수
|
||||||
|
function crossProduct(p1, p2, p3) {
|
||||||
|
const dx1 = p2.x - p1.x
|
||||||
|
const dy1 = p2.y - p1.y
|
||||||
|
const dx2 = p3.x - p2.x
|
||||||
|
const dy2 = p3.y - p2.y
|
||||||
|
return dx1 * dy2 - dy1 * dx2
|
||||||
|
}
|
||||||
|
|
||||||
|
let concaveIndices = [] //볼록한 부분 인덱스 배열
|
||||||
|
let concavePointIndices = [] //오목한 부분 인덱스 배열
|
||||||
|
|
||||||
|
// 오목한 부분 찾기
|
||||||
|
function findConcavePointIndices(points) {
|
||||||
|
let concaveIndices = []
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const p1 = points[i]
|
||||||
|
const p2 = points[(i + 1) % points.length]
|
||||||
|
const p3 = points[(i + 2) % points.length]
|
||||||
|
const cross = crossProduct(p1, p2, p3)
|
||||||
|
if (cross < 0) {
|
||||||
|
concaveIndices.push((i + 1) % points.length)
|
||||||
|
} else {
|
||||||
|
concavePointIndices.push((i + 1) % points.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return concaveIndices
|
||||||
|
}
|
||||||
|
|
||||||
|
// 오목한 부분 인덱스 찾기
|
||||||
|
concaveIndices = findConcavePointIndices(points) //오목한 부분을 제외한 인덱스
|
||||||
|
const concavePoints = concaveIndices.map((index) => points[index])
|
||||||
|
|
||||||
|
for (var i = 0; i < points.length; i++) {
|
||||||
|
var prev = points[(i - 1 + points.length) % points.length]
|
||||||
|
var current = points[i]
|
||||||
|
var next = points[(i + 1) % points.length]
|
||||||
|
|
||||||
|
// 두 벡터 계산 (prev -> current, current -> next)
|
||||||
|
var vector1 = { x: current.x - prev.x, y: current.y - prev.y }
|
||||||
|
var vector2 = { x: next.x - current.x, y: next.y - current.y }
|
||||||
|
|
||||||
|
// 벡터의 길이 계산
|
||||||
|
var length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y)
|
||||||
|
var length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y)
|
||||||
|
|
||||||
|
// 벡터를 단위 벡터로 정규화
|
||||||
|
var unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 }
|
||||||
|
var unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 }
|
||||||
|
|
||||||
|
// 법선 벡터 계산 (왼쪽 방향)
|
||||||
|
var normal1 = { x: -unitVector1.y, y: unitVector1.x }
|
||||||
|
var normal2 = { x: -unitVector2.y, y: unitVector2.x }
|
||||||
|
|
||||||
|
// 법선 벡터 평균 계산
|
||||||
|
var averageNormal = {
|
||||||
|
x: (normal1.x + normal2.x) / 2,
|
||||||
|
y: (normal1.y + normal2.y) / 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 평균 법선 벡터를 단위 벡터로 정규화
|
||||||
|
var lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y)
|
||||||
|
var unitNormal = {
|
||||||
|
x: averageNormal.x / lengthNormal,
|
||||||
|
y: averageNormal.y / lengthNormal,
|
||||||
|
}
|
||||||
|
|
||||||
|
let offsetX
|
||||||
|
let offsetY
|
||||||
|
|
||||||
|
if (concavePointIndices[0] === i || concavePointIndices[1] === i) {
|
||||||
|
// 인덱스가 배열이랑 같으면
|
||||||
|
if ((concavePointIndices[0] === 4 && concavePointIndices[1] === 5) || (concavePointIndices[0] === 2 && concavePointIndices[1] === 3)) {
|
||||||
|
offsetX = 1
|
||||||
|
offsetY = (offsetInputY / transformedMax) * originalMax * 2
|
||||||
|
} else {
|
||||||
|
offsetX = (offsetInputX / transformedMax) * originalMax * 2
|
||||||
|
offsetY = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offsetX = (offsetInputX / transformedMax) * originalMax * 2
|
||||||
|
offsetY = (offsetInputY / transformedMax) * originalMax * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 오프셋 적용
|
||||||
|
var offsetPoint = {
|
||||||
|
x1: current.x + unitNormal.x * offsetX,
|
||||||
|
y1: current.y + unitNormal.y * offsetY,
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetPoints.push(offsetPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
const outlinePolygon = makePolygon(offsetPoints)
|
||||||
|
|
||||||
|
// 아웃라인 폴리곤의 각 변을 선으로 생성
|
||||||
|
for (let i = 0; i < outlinePolygon.points.length; i++) {
|
||||||
|
const start = outlinePolygon.points[i]
|
||||||
|
const end = outlinePolygon.points[(i + 1) % outlinePolygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
|
||||||
|
|
||||||
|
const line = new QLine([start.x, start.y, end.x, end.y], {
|
||||||
|
stroke: 'blue',
|
||||||
|
strokeWidth: 2,
|
||||||
|
property: 'normal',
|
||||||
|
fontSize: 14,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 선을 배열에 추가
|
||||||
|
outLines.push(line)
|
||||||
|
canvas.add(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제
|
||||||
|
canvas?.remove(outLines[concavePointIndices[0]]) //가운데 제외되는 선을 지운다
|
||||||
|
|
||||||
|
//라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정
|
||||||
|
outLines.forEach((outline, index) => {
|
||||||
|
let minX, minY, maxX, maxY
|
||||||
|
if (outline.x2 < outline.x1 || outline.y2 < outline.y1) {
|
||||||
|
outLines[index].x1 = outline.x2
|
||||||
|
outLines[index].y1 = outline.y2
|
||||||
|
outLines[index].x2 = outline.x1
|
||||||
|
outLines[index].y2 = outline.y1
|
||||||
|
outLines[index].line.x1 = minX
|
||||||
|
outLines[index].line.y1 = minY
|
||||||
|
outLines[index].line.x2 = maxX
|
||||||
|
outLines[index].line.y2 = maxY
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// for (let i = 0; i < outLines.length; i++) {
|
||||||
|
// if (!i % 2 === 0) {
|
||||||
|
// if (i === concavePointIndices[0] - 1 || i === concavePointIndices[1] + 1) {
|
||||||
|
// //배열 3번이나 5번일때
|
||||||
|
// } else {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
let parallelLines = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기
|
||||||
|
if (parallelLines > outLines.length) {
|
||||||
|
parallelLines = outLines[concavePointIndices[0] + 4 - outLines.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 템플릿 B 적용
|
* 템플릿 B 적용
|
||||||
*/
|
*/
|
||||||
@ -2356,6 +2588,7 @@ export function useMode() {
|
|||||||
zoomOut,
|
zoomOut,
|
||||||
zoom,
|
zoom,
|
||||||
togglePolygonLine,
|
togglePolygonLine,
|
||||||
|
handleOuterlinesTest,
|
||||||
handleOuterlinesTest2,
|
handleOuterlinesTest2,
|
||||||
makeRoofPatternPolygon,
|
makeRoofPatternPolygon,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -450,7 +450,11 @@ export const sortedPoints = (points) => {
|
|||||||
// 짝수번째는 y값이 같은 점을 찾는다.
|
// 짝수번째는 y값이 같은 점을 찾는다.
|
||||||
for (let i = 0; i < copyPoints.length; i++) {
|
for (let i = 0; i < copyPoints.length; i++) {
|
||||||
// y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다.
|
// y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다.
|
||||||
const temp = copyPoints.filter((point) => point.y === currentPoint.y)
|
let temp = copyPoints.filter((point) => point.y === currentPoint.y)
|
||||||
|
if (temp.length === 0) {
|
||||||
|
// temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
|
||||||
|
temp = Array.of(findClosestPointByY(currentPoint, copyPoints))
|
||||||
|
}
|
||||||
// temp중 x값이 가장 가까운 값
|
// temp중 x값이 가장 가까운 값
|
||||||
|
|
||||||
const min = temp.reduce((prev, current) => (Math.abs(current.x - currentPoint.x) <= Math.abs(prev.x - currentPoint.x) ? current : prev))
|
const min = temp.reduce((prev, current) => (Math.abs(current.x - currentPoint.x) <= Math.abs(prev.x - currentPoint.x) ? current : prev))
|
||||||
@ -465,7 +469,12 @@ export const sortedPoints = (points) => {
|
|||||||
// 홀수번째는 x값이 같은 점을 찾는다.
|
// 홀수번째는 x값이 같은 점을 찾는다.
|
||||||
for (let i = 0; i < copyPoints.length; i++) {
|
for (let i = 0; i < copyPoints.length; i++) {
|
||||||
// x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다.
|
// x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다.
|
||||||
const temp = copyPoints.filter((point) => point.x === currentPoint.x)
|
let temp = copyPoints.filter((point) => point.x === currentPoint.x)
|
||||||
|
if (temp.length === 0) {
|
||||||
|
// temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
|
||||||
|
|
||||||
|
temp = Array.of(findClosestPointByX(currentPoint, copyPoints))
|
||||||
|
}
|
||||||
// temp중 y값이 가장 가까운 값
|
// temp중 y값이 가장 가까운 값
|
||||||
const min = temp.reduce((prev, current) => (Math.abs(current.y - currentPoint.y) <= Math.abs(prev.y - currentPoint.y) ? current : prev))
|
const min = temp.reduce((prev, current) => (Math.abs(current.y - currentPoint.y) <= Math.abs(prev.y - currentPoint.y) ? current : prev))
|
||||||
|
|
||||||
@ -517,6 +526,81 @@ export function findClosestLineToPoint(point, lines) {
|
|||||||
return closestLine
|
return closestLine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x값이 가장 가까운 점
|
||||||
|
* @param targetPoint
|
||||||
|
* @param points
|
||||||
|
* @returns {*|null}
|
||||||
|
*/
|
||||||
|
function findClosestPointByX(targetPoint, points) {
|
||||||
|
if (points.length === 0) {
|
||||||
|
return null // Return null if the points array is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
let closestPoint = points[0]
|
||||||
|
let smallestDistance = Math.abs(targetPoint.x - points[0].x)
|
||||||
|
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const currentDistance = Math.abs(targetPoint.x - points[i].x)
|
||||||
|
if (currentDistance < smallestDistance) {
|
||||||
|
smallestDistance = currentDistance
|
||||||
|
closestPoint = points[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y값이 가장 가까운 점
|
||||||
|
* @param targetPoint
|
||||||
|
* @param points
|
||||||
|
* @returns {*|null}
|
||||||
|
*/
|
||||||
|
function findClosestPointByY(targetPoint, points) {
|
||||||
|
if (points.length === 0) {
|
||||||
|
return null // Return null if the points array is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
let closestPoint = points[0]
|
||||||
|
let smallestDistance = Math.abs(targetPoint.y - points[0].y)
|
||||||
|
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const currentDistance = Math.abs(targetPoint.y - points[i].y)
|
||||||
|
if (currentDistance < smallestDistance) {
|
||||||
|
smallestDistance = currentDistance
|
||||||
|
closestPoint = points[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주어진 점에서 points 배열 중 가장 가까운 점을 찾는 함수입니다.
|
||||||
|
* @param {Object} targetPoint - { x: number, y: number } 형태의 대상 점 객체
|
||||||
|
* @param {Array} points - { x: number, y: number } 형태의 점 객체들의 배열
|
||||||
|
* @returns {Object} targetPoint에 가장 가까운 점 객체
|
||||||
|
*/
|
||||||
|
export function findClosestPoint(targetPoint, points) {
|
||||||
|
if (points.length === 0) {
|
||||||
|
return null // points 배열이 비어있으면 null 반환
|
||||||
|
}
|
||||||
|
|
||||||
|
let closestPoint = points[0]
|
||||||
|
let smallestDistance = calculateDistancePoint(targetPoint, closestPoint)
|
||||||
|
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const currentDistance = calculateDistancePoint(targetPoint, points[i])
|
||||||
|
if (currentDistance < smallestDistance) {
|
||||||
|
smallestDistance = currentDistance
|
||||||
|
closestPoint = points[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestPoint
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 점과 직선사이의 최단 거리
|
* 점과 직선사이의 최단 거리
|
||||||
* @param point
|
* @param point
|
||||||
@ -535,3 +619,13 @@ export function calculateDistance(point, line) {
|
|||||||
const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))
|
const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))
|
||||||
return numerator / denominator
|
return numerator / denominator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 두 점 사이의 거리를 계산하는 함수입니다.
|
||||||
|
* @param {Object} point1 - 첫 번째 점 { x: number, y: number }
|
||||||
|
* @param {Object} point2 - 두 번째 점 { x: number, y: number }
|
||||||
|
* @returns {number} 두 점 사이의 거리
|
||||||
|
*/
|
||||||
|
function calculateDistancePoint(point1, point2) {
|
||||||
|
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2))
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user