/// <summary> /// Face 의 면적을 계산한다. /// 단, 다각형의 면적 계산은 정확하지 않다. (개선 필요함) /// /// 면적계산을 상대좌표를 상관이 없기 때문에 사용한다. /// </summary> /// <returns>Face 면적</returns> public double calcArea(CFace face) { double dArea = 0; double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; /// Rectangle 은 4 좌표의 평균점을 사용한다. if (face.FaceType == EMFaceType.RECTANGLE) { face.getMinMaxX(ref minX, ref maxX); face.getMinMaxY(ref minY, ref maxY); /// 가로, 세로 곱 return((maxX - minX) * (maxY - minY)); } /// Polygon 의 다각형 면적 계산은 단순한 형상에서만 가능하고 오차가 있는 것으로 판단된다. /// 추후 : 검증하라. else { /// The last vertex is the 'previous' one to the first int j = face.RelativePointList.Count - 1; for (int i = 0; i < face.RelativePointList.Count; i++) { dArea += (face.RelativePointList[j].m_dX + face.RelativePointList[i].m_dX) * (face.RelativePointList[j].m_dY - face.RelativePointList[i].m_dY); j = i; /// j is previous vertex to i } return(Math.Abs(dArea / 2.0f)); } }
private void buttonOK_Click(object sender, EventArgs e) { /// 완벽한 입력인 상태에서만 Draw 및 저장이 가능한다. if (isInputDataOK() == false) { DialogResult = DialogResult.Cancel; return; } if (PartType == EMKind.COIL && FaceType == EMFaceType.POLYGON) { if (false == isRectangleShapeInPopup()) { if (CSettingData.m_emLanguage == EMLanguage.Korean) { CNotice.noticeWarning("Coil 의 형상이 직사각형이 아닙니다.\nCoil 지정을 취소합니다."); } else { CNotice.noticeWarning("The shape of the Coil is not rectangular.\n.Cancels the Coil assignment."); } DialogResult = DialogResult.Cancel; return; } } // 확인을 위해 임시 생성한다. CFace faceTemp = makeFaceInPopup(); if (faceTemp == null) { CNotice.noticeWarningID("TWAP1"); DialogResult = DialogResult.Cancel; return; } if (false == faceTemp.isShapeOK()) { CNotice.printLogID("TWAP3"); DialogResult = DialogResult.Cancel; return; } FormMain formMain = ((FormMain)this.Owner); if (formMain != null) { // 혹시 FEMM 의 화면이 닫힌 경우 FEMM 의 화면을 복원합니다. formMain.reopenFEMM(); } m_strPartName = textBoxPartName.Text; // 문제가 없으면 정상 종료를 리턴한다. DialogResult = DialogResult.OK; }
private CFace makeRectangleFaceInPopup() { double minX, minZ, maxX, maxZ; minX = minZ = 1e100; maxX = maxZ = -1e100; CFace face = new CFace(); if (false == isRectangleShapeInPopup()) { return(null); } foreach (CPointUI pointUI in ListPointUI) { if (minX > Convert.ToDouble(pointUI.StrCoordX)) { minX = Convert.ToDouble(pointUI.StrCoordX); } if (minZ > Convert.ToDouble(pointUI.StrCoordZ)) { minZ = Convert.ToDouble(pointUI.StrCoordZ); } if (maxX < Convert.ToDouble(pointUI.StrCoordX)) { maxX = Convert.ToDouble(pointUI.StrCoordX); } if (maxZ < Convert.ToDouble(pointUI.StrCoordZ)) { maxZ = Convert.ToDouble(pointUI.StrCoordZ); } } if (minX >= maxX || minZ >= maxZ) { return(null); } face.setRectanglePoints(minX, minZ, maxX, maxZ); return(face); }
/// <summary> /// Face 의 면적을 계산한다. /// /// 면적계산을 상대좌표를 상관이 없기 때문에 사용한다. public double calcArea(CFace face) { double dArea = 0; double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; /// Rectangle 은 4 좌표의 평균점을 사용한다. if (face.FaceType == EMFaceType.RECTANGLE) { face.getMinMaxX(ref minX, ref maxX); face.getMinMaxY(ref minY, ref maxY); /// 가로, 세로 곱 dArea = Math.Abs((maxX - minX) * (maxY - minY)); } // 다각형 면적계산 알고리즘 : https://mathworld.wolfram.com/PolygonArea.html else { // 가장 마지막 항 (xn*y1 과 yn*x1) 을 첫번째로 우선처리하기 위한 설정한다. // // - 계산 순서는 점이 3개라면 자료의 수식 순서와 달리 아래와 같이 마지막항이 가장 앞으로 위치한다. // x3y1 + x1y2 + x2y3 // y3x1 + y1x2 + y2x3 int j = face.RelativePointList.Count - 1; for (int i = 0; i < face.RelativePointList.Count; i++) { dArea += (face.RelativePointList[j].X * face.RelativePointList[i].Y) - (face.RelativePointList[j].Y * face.RelativePointList[i].X); j = i; /// j is previous vertex to i } dArea = Math.Abs(dArea / 2.0f); } return(dArea); }
private void buttonOK_Click(object sender, EventArgs e) { /// 완벽한 입력인 상태에서만 Draw 및 저장이 가능한다. if (isInputDataOK() == false) { return; } /// 형상 유효성 확인 /// CFace face = makeFace(); if (face == null) { CNotice.noticeWarningID("TWAP1"); return; } if (false == face.isShapeOK()) { CNotice.printTraceID("TWAP3"); return; } m_strPartName = textBoxPartName.Text; this.DialogResult = DialogResult.OK; FormMain formMain = ((FormMain)this.Owner); if (formMain == null) { CNotice.printTraceID("CNGM"); return; } // 혹시 FEMM 의 화면이 닫힌 경우 FEMM 의 화면을 복원합니다. formMain.reopenFEMM(); }
/// <summary> /// 형상 입력때나 수정때 형상을 다시 그린다 /// - 수정 부품만 빼고 타 부품들은 다시 그리고, 수정 부품은 창에 기록된 값을 그린다. /// </summary> /// <param name="pointControl">좌표점 PointControl</param> /// <param name="bRedraw">형상 수정이 없어도 강제로 ARC 변경때 강제로 수정함</param> internal void drawTemporaryFace(CPointUI pointControl, bool bRedraw = false) { try { /// [문제] /// - Form 에서는 Parent를 사용할 수 없어 Owner 속성을 사용하지만 /// 종종 Owner 가 null 로 넘어오는 문제가 발생한다. /// [해결] /// - PopupShape 창을 생성하기 전에 Owner 속성을 FormMain 으로 초기화 해 두어야 /// 확실하게 FormMain 을 얻을 수 있다. FormMain formMain = ((FormMain)this.Owner); if (formMain == null) { CNotice.printLogID("CNGM"); return; } CScriptFEMM femm = formMain.m_femm; CDataNode nodeParts = formMain.m_design.getNode(m_strPartName); /// 0. 해당 좌표점의 수정이 있었는지를 판단한다. /// - 수정이 있는 경우만 다시 그리기 위해서이다. bool retCheck = false; /// 초기 생성때는 이전 nodeParts 가 없음으로 사용하지 않고, 기존 노드의 수정때만 사용한다. if (nodeParts != null) { /// 좌표 Control 에 빈칸이 존재하는 지를 확인함 for (int i = 0; i < ListPointUI.Count; i++) { /// 해당 좌표점만 비교한다. /// 만약, Parts 의 모든 좌표점을 비교하면 다른 좌표점이 수정되었을때 나머지 좌표점의 수정이 없어도 다시 그리기가 된다. if (ListPointUI[i] == pointControl) { if (((CShapeParts)nodeParts).Face.RelativePointList[i].X != Double.Parse(ListPointUI[i].StrCoordX.Trim())) { retCheck = true; } if (((CShapeParts)nodeParts).Face.RelativePointList[i].Y != Double.Parse(ListPointUI[i].StrCoordZ.Trim())) { retCheck = true; } } } } // Arc 관련 이벤트 호출이면 강제로 다시그리기를 한다. if (bRedraw == true) { retCheck = true; } if (retCheck == true) { femm.deleteAll(); /// 1. 혹시 수정중이라면, 현재 작업 중인 Face 를 제외하고 형상 그리기 foreach (CDataNode node in formMain.m_design.GetNodeList) { if (node.GetType().BaseType.Name == "CShapeParts") { if (node.NodeName != m_strPartName) { ((CShapeParts)node).Face.drawFace(femm); } } } } /// 2. 현재 수정중이거나 생성중인 Face 의 형상 그리기 retCheck = true; /// 좌표 Control 에 빈칸이 존재하는 지를 확인함 for (int i = 0; i < ListPointUI.Count; i++) { if (ListPointUI[i].StrCoordX.Trim().Length == 0) { retCheck = false; } if (ListPointUI[i].StrCoordZ.Trim().Length == 0) { retCheck = false; } } double dBase_X, dBase_Y; dBase_X = dBase_Y = 0; dBase_X = Double.Parse(textBoxBaseX.Text.Trim()); dBase_Y = Double.Parse(textBoxBaseY.Text.Trim()); // 모든 데이터가 입력된 경우는 폐곡선의 정상적인 Face 를 그린다. if (retCheck == true) { CFace faceTemp = makeFaceInPopup(); if (null != faceTemp) { faceTemp.drawFace(femm); } else { CNotice.printLogID("TSWN"); } } // 모든 데이터가 아직 입력되지 않은 상태는 입력중인 데이터만으로 그림을 그린다. else { double dP1_X, dP1_Y, dP2_X, dP2_Y; bool bArc, bArcDirection; dP1_X = dP1_Y = dP2_X = dP2_Y = 0; bArc = bArcDirection = false; for (int i = 0; i < ListPointUI.Count; i++) { retCheck = true; if (ListPointUI[i].StrCoordX.Trim().Length == 0) { retCheck = false; } else { dP1_X = Double.Parse(ListPointUI[i].StrCoordX.Trim()) + dBase_X; } if (ListPointUI[i].StrCoordZ.Trim().Length == 0) { retCheck = false; } else { dP1_Y = Double.Parse(ListPointUI[i].StrCoordZ.Trim()) + dBase_Y; } /// X, Y 값이 모두 입력된 Point Control 인 경우 if (retCheck == true) { if (i == 0) { /// 사각형, 다각형 모두 적용된다. femm.drawPoint(dP1_X, dP1_Y); } else { if (this.FaceType == EMFaceType.RECTANGLE) { CNotice.printLogID("ATTW"); } /// 다각형만 적용된다. /// 만약 사각형의 경우 i = 1 까지 모두 채워지면 모두 입력된 상태로 if 문의 위에처 처리되기 때문이다. bArc = ListPointUI[i].IsArc; bArcDirection = ListPointUI[i].IsArcDirection; if (bArc == true) { femm.drawArc(dP2_X, dP2_Y, dP1_X, dP1_Y, bArcDirection); } else { femm.drawLine(dP2_X, dP2_Y, dP1_X, dP1_Y); } } // 이번 점을 이전 점으로 저장한다. dP2_X = dP1_X; dP2_Y = dP1_Y; } /// 채워지지 않은 좌표값을 발견하면 바로 빠져 나간다 else { break; } } } /// 선택된 좌표점을 붉은 색으로 표시한다. /// /// Base X, Y 변경 할때나 Leave 이벤트는 제외해야 함으로 null 을 넘겨오도록 되어 있다. if (pointControl != null) { /// XY 값 모두 들어 있는 경우에만 표시를 한다. if (pointControl.StrCoordX != "" && pointControl.StrCoordZ != "") { CPoint selectedPoint = new CPoint(); selectedPoint.X = Double.Parse(pointControl.StrCoordX) + dBase_X; selectedPoint.Y = Double.Parse(pointControl.StrCoordZ) + dBase_Y; femm.clearSelected(); femm.selectPoint(selectedPoint); } } } catch (Exception ex) { CNotice.printLog(ex.Message); } }
public void buttonDraw_Click(object sender, EventArgs e) { try { /// 완벽한 입력인 상태에서만 Draw 가 가능한다. bool retOK = isInputDataOK(); if (retOK == false) { return; } /// [문제] /// - Form 에서는 Parent를 사용할 수 없어 Owner 속성을 사용하지만 /// 종종 Owner 가 null 로 넘어오는 문제가 발생한다. /// [해결] /// - PopupShape 창을 생성하기 전에 Owner 속성을 FormMain 으로 초기화 해 두어야 /// 확실하게 FormMain 을 얻을 수 있다. FormMain formMain = ((FormMain)this.Owner); if (formMain == null) { CNotice.printLogID("CNGM"); return; } /// 형상 유효성 확인을 위해 임시로 생성한다. /// CFace faceTemp = makeFaceInPopup(); if (faceTemp == null) { CNotice.noticeWarningID("TWAP1"); return; } if (false == faceTemp.isShapeOK()) { CNotice.printLogID("TWAP3"); return; } CScriptFEMM femm = formMain.m_femm; femm.deleteAll(); /// 1. 작업 중인 Face 를 제외하고 형상 그리기 foreach (CDataNode node in formMain.m_design.GetNodeList) { if (node.GetType().BaseType.Name == "CShapeParts") { if (node.NodeName != m_strPartName) { ((CShapeParts)node).Face.drawFace(femm); } } } // FEMM 을 최상위로 올린다. CProgramFEMM.showFEMM(); // 혹시 FEMM 의 화면이 닫힌 경우 FEMM 의 화면을 복원합니다. formMain.reopenFEMM(); /// 2. 작업중인 Face 형상 그리기 faceTemp.drawFace(femm); } catch (Exception ex) { CNotice.printLog(ex.Message); } }
// Base Point 와 상대좌표로 좌표값이 저장되는 Face 가 생성된다. public CFace makeFaceInPopup() { try { CFace face = new CFace(); face.BasePoint.X = Double.Parse(textBoxBaseX.Text); face.BasePoint.Y = Double.Parse(textBoxBaseY.Text); if (FaceType == EMFaceType.RECTANGLE) { if (ListPointUI.Count != 2) { CNotice.printLogID("TATP1"); return(null); } double x1, y1, x2, y2; x1 = Double.Parse(ListPointUI[0].StrCoordX); y1 = Double.Parse(ListPointUI[0].StrCoordZ); x2 = Double.Parse(ListPointUI[1].StrCoordX); y2 = Double.Parse(ListPointUI[1].StrCoordZ); face.setRectanglePoints(x1, y1, x2, y2); } else { if (ListPointUI.Count < 4) { CNotice.printLogID("TANM"); return(null); } // PartType 가 코일이고 Polygon 형상을 가지고 있는 경우라면 (DXF로 읽어드리고 코일로 지정하는 경우) // Rectangle 로 바꾸어 저장한다. // 만약, Retangle 조건이 아니라면 지나쳐서 Polygon 으로 저장한다. if (PartType == EMKind.COIL) { CFace retFace = makeRectangleFaceInPopup(); if (retFace != null) { return(retFace); } } List <CPoint> listPoint = new List <CPoint>(); foreach (CPointUI pointControl in ListPointUI) { // 매번 신규로 생성을 해야 한다. CPoint point = new CPoint(); point.X = Double.Parse(pointControl.StrCoordX); point.Y = Double.Parse(pointControl.StrCoordZ); if (pointControl.IsArc == true) { point.LineKind = EMLineKind.ARC; } else { point.LineKind = EMLineKind.STRAIGHT; } if (pointControl.IsArcDirection == true) { point.DirectionArc = EMDirectionArc.BACKWARD; } else { point.DirectionArc = EMDirectionArc.FORWARD; } listPoint.Add(point); } face.setPolygonPoints(listPoint); } return(face); } catch (Exception ex) { CNotice.printLog(ex.Message); return(null); } }
/// <summary> /// Face 의 형상 정보가 있는 경우 호출되는 생성자로 /// TreeView 에서 파트를 더블클릭할때 호출되는 생성자이다. /// </summary> public PopupShape(string partName, CFace face, EMKind emKind) { InitializeComponent(); m_strPartName = partName; m_partType = emKind; m_bCreatePopupWindow = false; try { switch (m_partType) { case EMKind.COIL: labelPartName.Text = "Coil Name :"; this.Text = "Change Coil"; break; case EMKind.MAGNET: labelPartName.Text = "Magnet Name :"; this.Text = "Change Magnet"; break; case EMKind.STEEL: labelPartName.Text = "Steel Name :"; this.Text = "Change Steel"; break; case EMKind.NON_KIND: labelPartName.Text = "Part Name"; this.Text = "Change Part"; break; default: CNotice.printLogID("TPTI"); return; } if (face == null) { CNotice.printLogID("CTPS1"); return; } textBoxBaseX.Text = face.BasePoint.X.ToString(); textBoxBaseY.Text = face.BasePoint.Y.ToString(); if (face.getPointCount() < MIN_POLYGON_CONTROL_COUNT) { CNotice.printLogID("CTPS"); return; } /// 파트이름을 표시만하고 수정을 하지 못하게 한다. textBoxPartName.Text = partName; if (face.FaceType == EMFaceType.RECTANGLE) { // 설정과 동시에 이벤트가 호출되고 // 이벤트함수에서 FaceType 가 지정되면서 Popup 창의 형태와 UserControl 이 초기화 된다. comboBoxFaceType.SelectedItem = EMFaceType.RECTANGLE.ToString(); this.ListPointUI[0].StrCoordX = face.RelativePointList[0].X.ToString(); this.ListPointUI[0].StrCoordZ = face.RelativePointList[0].Y.ToString(); this.ListPointUI[1].StrCoordX = face.RelativePointList[2].X.ToString(); this.ListPointUI[1].StrCoordZ = face.RelativePointList[2].Y.ToString(); } else if (face.FaceType == EMFaceType.POLYGON) { // 설정과 동시에 이벤트가 호출되고 // 이벤트함수에서 FaceType 가 지정되면서 Popup 창의 형태와 UserControl 이 초기화 된다. comboBoxFaceType.SelectedItem = EMFaceType.POLYGON.ToString(); for (int i = 0; i < face.getPointCount(); i++) { // 기본 생성 Control 수 보다 작을 때는 있는 Control 에 데이터를 담고 // 클 때는 Control 를 생성하면서 데이터를 담은다. if (i >= MIN_POLYGON_CONTROL_COUNT) { this.addPointControl(new CPointUI(), true, this.panelPointControl); } this.ListPointUI[i].StrCoordX = face.RelativePointList[i].X.ToString(); this.ListPointUI[i].StrCoordZ = face.RelativePointList[i].Y.ToString(); if (face.RelativePointList[i].LineKind == EMLineKind.ARC) { this.ListPointUI[i].IsArc = true; } else { this.ListPointUI[i].IsArc = false; } if (face.RelativePointList[i].DirectionArc == EMDirectionArc.BACKWARD) { this.ListPointUI[i].IsArcDirection = true; } else { this.ListPointUI[i].IsArcDirection = false; } } } else { CNotice.printLogID("UAWF"); return; } /// 수정을 할때는 형상 과 Node Type 변경을 못하도록 한다. comboBoxFaceType.Enabled = false; comboBoxNodeType.SelectedItem = m_partType.ToString(); if (m_partType != EMKind.NON_KIND) { comboBoxNodeType.Enabled = false; } } catch (Exception ex) { CNotice.printLog(ex.Message); } }
/// <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); } }
// Base Point 와 상대좌표로 좌표값이 저장되는 Face 가 생성된다. public CFace makeFace() { try { CFace face = new CFace(); face.BasePoint.m_dX = Double.Parse(textBoxBaseX.Text); face.BasePoint.m_dY = Double.Parse(textBoxBaseY.Text); if (FaceType == EMFaceType.RECTANGLE) { if (ListPointControl.Count != 2) { CNotice.printTraceID("TATP1"); return(null); } double x1, y1, x2, y2; x1 = Double.Parse(ListPointControl[0].StrCoordX); y1 = Double.Parse(ListPointControl[0].StrCoordY); x2 = Double.Parse(ListPointControl[1].StrCoordX); y2 = Double.Parse(ListPointControl[1].StrCoordY); face.setRectanglePoints(x1, y1, x2, y2); } else { if (ListPointControl.Count < 4) { CNotice.printTraceID("TANM"); return(null); } List <CPoint> listPoint = new List <CPoint>(); foreach (CPointControl pointControl in ListPointControl) { // 매번 신규로 생성을 해야 한다. CPoint point = new CPoint(); point.m_dX = Double.Parse(pointControl.StrCoordX); point.m_dY = Double.Parse(pointControl.StrCoordY); if (pointControl.IsArc == true) { point.m_emLineKind = EMLineKind.ARC; } else { point.m_emLineKind = EMLineKind.STRAIGHT; } if (pointControl.IsArcDirection == true) { point.m_emDirectionArc = EMDirectionArc.BACKWARD; } else { point.m_emDirectionArc = EMDirectionArc.FORWARD; } listPoint.Add(point); } face.setPolygonPoints(listPoint); } return(face); } catch (Exception ex) { CNotice.printTrace(ex.Message); 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); } }