/// <summary> /// 두 라인의 교차를 확인한다. /// 단, 한 선의 점이 다른 선위에 올라가거나, 다른 선의 점과 겹치는 것은 고려하지 않는다. /// </summary> public bool isIntersected(CLine firstLine, CLine secondLine) { double dFL_P1_X = roundDigitOfShape(firstLine.m_startPoint.X); double dFL_P1_Y = roundDigitOfShape(firstLine.m_startPoint.Y); double dFL_P2_X = roundDigitOfShape(firstLine.m_endPoint.X); double dFL_P2_Y = roundDigitOfShape(firstLine.m_endPoint.Y); double dSL_P1_X = roundDigitOfShape(secondLine.m_startPoint.X); double dSL_P1_Y = roundDigitOfShape(secondLine.m_startPoint.Y); double dSL_P2_X = roundDigitOfShape(secondLine.m_endPoint.X); double dSL_P2_Y = roundDigitOfShape(secondLine.m_endPoint.Y); /// 알고리즘 2 번 /// /// ">" 를 ">=" 로 변경한 것은 꼭지점끼리 만나는 경우는 교차로 취급하지 않기 위해서이다. if (((dFL_P1_X - dFL_P2_X) * (dSL_P1_Y - dFL_P1_Y) + (dFL_P1_Y - dFL_P2_Y) * (dFL_P1_X - dSL_P1_X)) * ((dFL_P1_X - dFL_P2_X) * (dSL_P2_Y - dFL_P1_Y) + (dFL_P1_Y - dFL_P2_Y) * (dFL_P1_X - dSL_P2_X)) >= 0.0) { return(false); } if (((dSL_P1_X - dSL_P2_X) * (dFL_P1_Y - dSL_P1_Y) + (dSL_P1_Y - dSL_P2_Y) * (dSL_P1_X - dFL_P1_X)) * ((dSL_P1_X - dSL_P2_X) * (dFL_P2_Y - dSL_P1_Y) + (dSL_P1_Y - dSL_P2_Y) * (dSL_P1_X - dFL_P2_X)) >= 0.0) { return(false); } return(true); }
/// <summary> /// 두 라인의 접촉을 확인한다. /// /// 하나의 선에 다른 선의 두점 중 한점이라도 올라탔는지를 확인해서 접촉을 판단한다. /// </summary> public bool isContacted(CLine firstLine, CLine secondLine) { // 첫번째 라인에 두번째 라인의 양점이 올라갔는지를 판단한다. if (true == isPerchedOnLine(firstLine, secondLine.m_startPoint)) { return(true); } if (true == isPerchedOnLine(firstLine, secondLine.m_endPoint)) { return(true); } // 두번째 라인에 첫번째 라인의 양점이 올라갔는지를 판단한다. if (true == isPerchedOnLine(secondLine, firstLine.m_startPoint)) { return(true); } if (true == isPerchedOnLine(secondLine, firstLine.m_endPoint)) { return(true); } return(false); }
/// <summary> /// 두 라인의 겹침을 확인한다. /// /// 두선이 같은 직선위에 있는지를 판단하여 겹침을 검사한다. /// 단, 같은 직선위에 있더라도 떨어져 있는 직선일 수 있기 때문에 양점의 X 좌표를 비교해서 겹치는 구간이 있는지 판단한다. /// /// </summary> public bool isOverlaped(CLine firstLine, CLine secondLine) { double dFL_P1_X = firstLine.m_startPoint.X; double dFL_P1_Y = firstLine.m_startPoint.Y; double dFL_P2_X = firstLine.m_endPoint.X; double dFL_P2_Y = firstLine.m_endPoint.Y; double dSL_P1_X = secondLine.m_startPoint.X; double dSL_P1_Y = secondLine.m_startPoint.Y; double dSL_P2_X = secondLine.m_endPoint.X; double dSL_P2_Y = secondLine.m_endPoint.Y; /// 첫번째 라인의 직선방정식 기울기와 Y 절편을 계산한다. double dA = (dFL_P2_Y - dFL_P1_Y) / (dFL_P2_X - dFL_P1_X); double dB = dFL_P1_Y - dA * (dFL_P1_X); /// 첫번째 라인의 직선방정식에 두번째 라인 두점의 X 값을 입력하여 Y 값을 계산한다. double dStartY = dA * dSL_P1_X + dB; double dEndY = dA * dSL_P2_X + dB; double dBigX = Math.Max(dFL_P1_X, dFL_P2_X); double dSmallX = Math.Min(dFL_P1_X, dFL_P2_X); // 계산된 Y 값과 실제 두번째 라인 두점의 값이 일치하면 // 하나의 직선 방정식 위에 두개의 라인이 존재한다고 판단할 수 있다. if (isEqual(dStartY, dSL_P1_Y) && isEqual(dEndY, dSL_P2_Y)) { // 하나의 직선 방정식 위에 두개의 라인이 존재하더라도 두 라인이 떨어져 있을 수 있다. // 하나의 직선 방정식 위에 존재하면서 두개의 라인이 겹치는지를 아래의 조건을 판단하고 있다. // ( 방법은 두번째 라인의 두점의 X 값이 첫번째 라인의 X 값들 사이에 있는지로 검사하고 있다.) if (roundDigitOfShape(dBigX) >= roundDigitOfShape(dSL_P1_X) && roundDigitOfShape(dSmallX) <= roundDigitOfShape(dSL_P1_X) || roundDigitOfShape(dBigX) >= roundDigitOfShape(dSL_P2_X) && roundDigitOfShape(dSmallX) <= roundDigitOfShape(dSL_P2_X)) { return(true); } else { return(false); } } else { return(false); } }
/// <summary> /// 선 위에 다른 점이 올라가 있는 지를 검사한다. /// </summary> public bool isPerchedOnLine(CLine line, CPoint point) { double dL_P1_X = line.m_startPoint.X; double dL_P1_Y = line.m_startPoint.Y; double dL_P2_X = line.m_endPoint.X; double dL_P2_Y = line.m_endPoint.Y; double dP_X = point.X; double dP_Y = point.Y; /// 라인의 X 구간 double dBigX = Math.Max(dL_P1_X, dL_P2_X); double dSmallX = Math.Min(dL_P1_X, dL_P2_X); /// 라인의 Y 구간 double dBigY = Math.Max(dL_P1_Y, dL_P2_Y); double dSmallY = Math.Min(dL_P1_Y, dL_P2_Y); /// 직선이 수직선인 경우는 예외 처리한다. if (isEqual(dL_P2_X, dL_P1_X)) { /// 점의 X 좌표가 직선의 X 좌표와 일치하면 라인 위의 점이다. if (isEqual(dL_P1_X, dP_X)) { if (roundDigitOfShape(dBigY) >= roundDigitOfShape(dP_Y) && roundDigitOfShape(dSmallY) <= roundDigitOfShape(dP_Y)) { return(true); } } } /// 직선이 수평선인 경우는 예외 처리한다. else if (isEqual(dL_P2_Y, dL_P1_Y)) { /// 점의 Y 좌표가 직선의 Y 좌표와 일치하면 라인 위의 점이다. if (isEqual(dL_P1_Y, dP_Y)) { if (roundDigitOfShape(dBigX) >= roundDigitOfShape(dP_X) && roundDigitOfShape(dSmallX) <= roundDigitOfShape(dP_X)) { return(true); } } } else { /// 라인의 직선방정식 기울기와 Y 절편을 계산한다. double dL_A = (dL_P2_Y - dL_P1_Y) / (dL_P2_X - dL_P1_X); double dL_B = dL_P1_Y - dL_A * (dL_P1_X); /// 라인의 직선방정식에 점의 X 값을 입력하여 Y 값을 계산한다. double dP_Calc_Y = dL_A * dP_X + dL_B; /// 계산된 Y 값과 좌표 Y 값이 일치하면 직선의 방정식위에 있는 점이다. if (isEqual(dP_Calc_Y, dP_Y)) { /// 직선의 방정식 위에 있는 점 중에서 실제 라인 위에 있는 점인지를 판단한다. if ((roundDigitOfShape(dBigX) >= roundDigitOfShape(dP_X) && roundDigitOfShape(dSmallX) <= roundDigitOfShape(dP_X)) && (roundDigitOfShape(dBigY) >= roundDigitOfShape(dP_Y) && roundDigitOfShape(dSmallY) <= roundDigitOfShape(dP_Y))) { return(true); } } } return(false); }
/// <summary> /// 재귀호출을 사용해서 FACE 내부점을 찾아낸다. /// 재질 블럭점을 찾는 것이기 때문에 절대좌표를 사용해야 한다. /// /// 참고 사이트 : http://bowbowbow.tistory.com/24 /// </summary> /// <param name="minX">내부점을 찾는 구간의 X 점 최소값</param> /// <param name="maxX">내부점을 찾는 구간의 X 점 최대값</param> /// <param name="baseY">내부 좌표값을 찾은 Y 위치 </param> /// <returns></returns> public CPoint findInsidePoint(CFace face, double minX, double maxX, double minY, double maxY) { int nRightIntersection = 0; int nLeftIntersection = 0; int nRightPerchedPointOnCheckLine = 0; int nLeftPerchedPointOnCheckLine = 0; int nPerchedCenterPointOnFaceLine = 0; CLine rightCheckLine = new CLine(); CLine leftCheckLine = new CLine(); CPoint centerPoint = new CPoint(); double baseY = (minY + maxY) / 2.0f; double centerX = (minX + maxX) / 2.0f; //// 너무 작은 소수점 이하는 정리한다. minX = roundDigitOfShape(minX); maxX = roundDigitOfShape(maxX); baseY = roundDigitOfShape(baseY); // 중심점을 만든다. centerPoint.X = centerX; centerPoint.Y = baseY; /// create a right check line rightCheckLine.m_startPoint.X = centerX; rightCheckLine.m_startPoint.Y = baseY; rightCheckLine.m_endPoint.X = maxX + 10.0f; // 오른 검색선의 끝을 maxX 보다 10 크게 한다. rightCheckLine.m_endPoint.Y = baseY; /// create a left check line leftCheckLine.m_startPoint.X = centerX; leftCheckLine.m_startPoint.Y = baseY; leftCheckLine.m_endPoint.X = minX - 10.0f; // 오른 검색선의 끝을 minX 보다 10 작게 한다. leftCheckLine.m_endPoint.Y = baseY; /// 매번 생성하는 Property 이기 때문에 /// LineList 는 새로운 List에 담는 동작 한번만 호출하고, 사용은 새로운 List 를 사용한다. List <CLine> listAbsoluteLine = new List <CLine>(); listAbsoluteLine = face.AbsoluteLineList; foreach (CLine line in listAbsoluteLine) { /// Face 선들과 검색 선의 교차 횟수 확인 if (true == isIntersected(line, rightCheckLine)) { nRightIntersection++; } if (true == isIntersected(line, leftCheckLine)) { nLeftIntersection++; } /// Face 선의 양점이 검색 선 위에 올라가 있는지 확인 if (true == isPerchedOnLine(rightCheckLine, line.m_startPoint)) { nRightPerchedPointOnCheckLine++; } if (true == isPerchedOnLine(rightCheckLine, line.m_endPoint)) { nRightPerchedPointOnCheckLine++; } if (true == isPerchedOnLine(leftCheckLine, line.m_startPoint)) { nLeftPerchedPointOnCheckLine++; } if (true == isPerchedOnLine(leftCheckLine, line.m_endPoint)) { nLeftPerchedPointOnCheckLine++; } /// Face 선위에 중심점이 올라가 있는지 확인 if (true == isPerchedOnLine(line, centerPoint)) { nPerchedCenterPointOnFaceLine++; } } //------------------------------------------------------------------- // 내부점 판단 // // 내부점을 만족하면 Point 를 리턴하고, // 만족하지 못하면 측정 면적을 수정하고 재귀호출을 통해 Point 를 찾아낸다. //------------------------------------------------------------------ // CPoint point = new CPoint(); // Center 점이 Face Line 위에 올라가 있으면 우측 1/2 사각형에서 내부점을 찾는다. if (nPerchedCenterPointOnFaceLine > 0) { return(findInsidePoint(face, centerX, maxX, minY, maxY)); } // Face Line 의 양점이 우측 검색 라인에 올라가는 경우는 상측 1/2 사각형에서 내부점을 찾는다. if (nRightPerchedPointOnCheckLine > 0 || nLeftPerchedPointOnCheckLine > 0) { return(findInsidePoint(face, minX, maxX, baseY, maxY)); } // 양측이 홀수이면 Inside Point 이다. if (EMNumberKind.ODD == getNumberKind(nRightIntersection) && EMNumberKind.ODD == getNumberKind(nLeftIntersection)) { point.X = centerX; point.Y = baseY; return(point); } /// 왼쪽이 짝수이면 X 값의 최소값과 중심값 사이의 중점을 다시 확인한다. else if (EMNumberKind.EVEN == getNumberKind(nLeftIntersection)) { return(findInsidePoint(face, minX, centerX, minY, maxY)); } /// 오른쪽이 짝수이면 X 값의 중심값과 최대값 사이의 중점을 다시 확인한다. else if (EMNumberKind.EVEN == getNumberKind(nRightIntersection)) { return(findInsidePoint(face, centerX, maxX, minY, maxY)); } else { /// Block Point 를 찾기 위해서 findInsidePoint() 를 호출할 때 /// Face 형상의 문제로 오류가 발생하여 Face 바깥의 지점으로 계산이 리턴되면 /// Block Point 가 추가되지 못해서 /// FEMM 에서 Block Point 에 재질 인가 할 때 다른 Block Point 에 인가되는 문제가 발생한다. /// /// 따라서 findInsidePoint() 에서 내부점을 찾지 못할 때는 /// 중심의 좌표값을 넘기지 않고 null 을 리턴하여 Block Point 재질 설정 동작을 막는다. CNotice.noticeWarningID("FTCT"); return(null); } }
/// <summary> /// 재귀호출을 사용해서 FACE 내부점을 찾아낸다. /// 재질 블럭점을 찾는 것이기 때문에 절대좌표를 사용해야 한다. /// /// 참고 사이트 : http://bowbowbow.tistory.com/24 /// </summary> /// <param name="minX">내부점을 찾는 구간의 X 점 최소값</param> /// <param name="maxX">내부점을 찾는 구간의 X 점 최대값</param> /// <param name="baseY">내부 좌표값을 찾은 Y 위치 </param> /// <returns></returns> public CPoint findInsidePoint(CFace face, double minX, double maxX, double baseY) { int nRightIntersection = 0; int nLeftIntersection = 0; int nRightPerchedPoint = 0; int nLeftPerchedPoint = 0; // 자리수 정리 minX = round(minX); maxX = round(maxX); baseY = round(baseY); double centerX = (minX + maxX) / 2.0f; CLine rightLine = new CLine(); CLine leftLine = new CLine(); /// create a right check line rightLine.m_startPoint.m_dX = centerX; rightLine.m_startPoint.m_dY = baseY; rightLine.m_endPoint.m_dX = maxX + 10.0f; // 오른 검색선의 끝을 maxX 보다 10 크게 한다. rightLine.m_endPoint.m_dY = baseY; /// create a left check line leftLine.m_startPoint.m_dX = centerX; leftLine.m_startPoint.m_dY = baseY; leftLine.m_endPoint.m_dX = minX - 10.0f; // 오른 검색선의 끝을 minX 보다 10 작게 한다. leftLine.m_endPoint.m_dY = baseY; /// 매번 생성하는 Property 이기 때문에 /// LineList 는 새로운 List에 담는 동작 한번만 호출하고, 사용은 새로운 List 를 사용한다. List <CLine> listAbsoluteLine = new List <CLine>(); listAbsoluteLine = face.AbsoluteLineList; /// Face 의 선과 검색선의 교차점을 찾는다. foreach (CLine line in listAbsoluteLine) { if (true == isIntersected(line, rightLine)) { nRightIntersection++; } if (true == isIntersected(line, leftLine)) { nLeftIntersection++; } } /// 교차를 검사할때 Face 선의 양점은 고려하지 않는다. /// 따라서 검색선에 Face 선의 점을 지나치는 경우는 교차점이 인식되지 못한다. /// 라인의 양점이 검색선에 올가가는지도 추가로 검색한다. /// foreach (CLine line in listAbsoluteLine) { // 만약 시작과 끝이 같이 올라간 경우라면 검색선에 Face 선이 올라간 경우로 검색에서 제외한다. // 라인의 한점만 올라간 경우를 Perched Point 로 사용한다. if (true == isPerchedOnLine(rightLine, line.m_startPoint) && true == isPerchedOnLine(rightLine, line.m_endPoint)) { nRightPerchedPoint += 0; } else if (true == isPerchedOnLine(rightLine, line.m_startPoint)) { nRightPerchedPoint++; } else if (true == isPerchedOnLine(rightLine, line.m_endPoint)) { nRightPerchedPoint++; } if (true == isPerchedOnLine(leftLine, line.m_startPoint) && true == isPerchedOnLine(leftLine, line.m_endPoint)) { nLeftPerchedPoint += 0; } else if (true == isPerchedOnLine(leftLine, line.m_startPoint)) { nLeftPerchedPoint++; } else if (true == isPerchedOnLine(leftLine, line.m_endPoint)) { nLeftPerchedPoint++; } } if ((nRightPerchedPoint % 2 != 0) || (nLeftPerchedPoint % 2 != 0)) { CNotice.printTrace("findInsidePoint 에서 PerchedPoint 값이 홀수가 되었습니다."); } /// 점이 올라가는 경우 두점이 같이 올라가기 때문에 한번 교차에 두번 카운팅이 된다. /// 따라서 1/2 로 처리한다. /// nRightIntersection += (int)(nRightPerchedPoint / 2.0f); nLeftIntersection += (int)(nLeftPerchedPoint / 2.0f); CPoint point = new CPoint(); /// 양측이 홀수이면 Inside Point 이다. if (EMNumberKind.ODD == getNumberKind(nRightIntersection) && EMNumberKind.ODD == getNumberKind(nLeftIntersection)) { point.m_dX = centerX; point.m_dY = baseY; return(point); } /// 왼쪽이 짝수이면 X 값의 최소값과 중심값 사이의 중점을 다시 확인한다. else if (EMNumberKind.EVEN == getNumberKind(nLeftIntersection)) { return(findInsidePoint(face, minX, centerX, baseY)); } /// 오른쪽이 짝수이면 X 값의 중심값과 최대값 사이의 중점을 다시 확인한다. else if (EMNumberKind.EVEN == getNumberKind(nRightIntersection)) { return(findInsidePoint(face, centerX, maxX, baseY)); } else { /// Block Point 를 찾기 위해서 findInsidePoint() 를 호출할 때 /// Face 형상의 문제로 오류가 발생하여 Face 바깥의 지점으로 계산이 리턴되면 /// Block Point 가 추가되지 못해서 /// FEMM 에서 Block Point 에 재질 인가 할 때 다른 Block Point 에 인가되는 문제가 발생한다. /// /// 따라서 findInsidePoint() 에서 내부점을 찾지 못할 때는 /// 중심의 좌표값을 넘기지 않고 null 을 리턴하여 Block Point 재질 설정 동작을 막는다. CNotice.noticeWarningID("FTCT"); return(null); } }