기본 콘텐츠로 건너뛰기

1월, 2021의 게시물 표시

[S3D] CableWay, CableTray의 OrientedRangeBox 구하기

 S3D의 객체에는 RangeBox라는 객체의 크기를 가지는 속성이 있습니다. RangeBox는 글로벌 좌표계에서 Min, Max 좌표를 가지고 있습니다. 객체가 $X$축,$Y$축 또는 $Z$축에 놓여있을 때는 RangeBox와 실제 크기가 일치하겠지만, 그 외의 경우에는 RangeBox의 크기가 실제 크기보다 큽니다. 따라서 모든 경우에 객체 크기에 정확히 일치하는 크기를 구할때 RangeBox는 부적합합니다. 심지어 $X$축,$Y$축 또는 $Z$축에 놓여있을 때에도 RangeBox가 실제 크기보다 조금 더 큽니다. 모든 경우에 객체의 실제 크기와 같은 RangeBox를 구하기 위해서는 OrientedRangeBox를 구해야 합니다. 왜 S3D에서 객체의 속성으로 OrientedRangeBox를 제공하지 않는지 모르겠습니다. OrientedRangeBox는 아래와 같은 구조를 가지고 있습니다. Cable Way/Cable Tray 객체는 Start, End, 코너의 8개점, Length, Width, Depth 정보를 가지있습니다. 8개의 코너 점은 실제 형상보다 큰 지점을 가리키고 있고 Start, End는 실제 형상의 단면의 중점입니다. 코너의 8개의 점들은 객체를 이루는 두 단면의 4개씩의 점들로 이루어진다고 생각합니다. 제가 디자인했다면 이렇게 했을겁니다. 아무도 뒤죽박죽 점들의 순서를 정하지 않았을겁니다. 그렇지 않다면 제 손에 장을 지지겠습니다. 이제 이 8개의 점들로 객체의 Width, Depth 벡터를 구하면 OrientedRangeBox의 3 벡터를 결정할 수 있습니다.  $\overrightarrow{P_1-P_0}$과 $\overrightarrow{P_3-P_0}$ 중에서 객체의 $\overrightarrow{Width},\overrightarrow{Depth}$를 선택해야 합니다. 객체의 Width ,Depth 크기를 알고 있으니 $\overrightarrow{P_1-P_0}$와 $\overrightarrow{P_3-P_0}$의 크기를

[S3D] 프로젝트 분투기

 발등에 불이 떨어졌습니다. 현재 진행 중인 2월 중간 보고에 고객사 부사장님이 참석한다고 합니다. 이미 2개의 프로젝트가 절뚝거리며 진행하고 있기 때문에 지금 프로젝트는 제대로 해야합니다. 네트워크 망 구성과 최단 거리 찾기(Dijkstra 기반) 알고리즘에 대한 코드를 정리한 후에 김** 차장과 결과를 확인하기로 했습니다. 이렇게 From, To Cable Way를 선택한 후에 버튼을 눌러 결과가 나오기를 기다렸습니다. 1분가량의 시간이 흘렀습니다. 드디어 프로그램에서 From에서 To로 가는 4,128개의 경로를 찾았습니다. 우리가 생각하지도 못한 길을 찾다니 하고 감탄하고 있을때가 아닙니다. 무려 4,128개의 경로라니요, 눈으로 보기에는 고작 대여섯개의 경로만 보이는데 말입니다. 우리의 예측과 실제 결과가 다를때에는 그 원인을 찾아야 합니다. 고단하고 손가락 아프고 눈이 시린 디버깅의 시작입니다. 반드시 4,128개 경로들의 차이점을 찾아야 합니다. 어... Cable Way를 구성하는 Feature의 RangeBox가 실제 형상보다 크게 잡히는 것을 확인했습니다. 실제 형상과 동일한 RangeBox를 예상하고 있었는데 우리의 예상이 빗나갔습니다. RangeBox 우리의 잘못이 아닙니다. 지랄맞은 S3D입니다. Insulation, Maintenance, Operation Aspect 때문일까봐 켜보고 다시 확인해봤지만 결과는 변함이 없습니다. 밤은 늦었지만 어쩔수 없이 김** 차장에게 전화해 물어봤지만 뾰족한 방안은 없습니다. 모든 주어진 데이타와 대학교 1학년때까지 쌓은 수학적 지식을 총동원하여(사실 중학교때까지의 지식만으로도 충분했습니다.) 형상에 딱 맞는 RangeBox(실제는 OrientedRangeBox)를 구했습니다. Cable Way Feature 형상에 딱 맞는 OrientedRangeBox 구하는 방법은 다음 글에서 이야기하도록 하겠습니다. 밤 9시가 넘어가고 있습니다. 집중력이 떨어지고 있습니다. 나이들어 야근은 무리라는 걸 실감하

[Angular] 라이선스 웹페이지 구축

 라이선스 웹페이지 구축에 Angular를 선택한 이유는 나름 최신 기술이고 구글에서 관리하는 프레임워크라 믿을 수 있다는 점 그리고 컴포넌트 기반이라 코드의 재사용이 가능하다는 점 때문이었습니다. 또한 Angular는 TypeScript라는 JavaScript의 상위 언어를 사용하고 있습니다. Angular외에 React라는 JavaScript 기반의 라이브러리도 있습니다. 둘의 차이점은 아래 표에서 확인해 보세요.

Networkx 라이브러리 활용법

  Networkx 라는 그래프에 관련된 훌륭한 라이브러리가 있습니다. 이 라이브러리에 관련된 여러 글들을 인터넷에서 찾아보고 지금 진행하고 있는 Cable Auto Routing에 적용할 수 있다는 것을 알게되었습니다. Cable Auto Routing은 두 장치를 연결하는 Cable의 최단 경로를 자동으로 찾는 것입니다. Networkx 의 shortest_path라는 함수를 이용하여 출발 노드에서 종료 노드에 도달하는 최단 경로를 구할 수 있습니다. $$path = \text{nx.dijkstra_path}(G, \text{start_node}, \text{end_node}, weight)$$ 아마 shortest_path 함수도 내부적으로 dijkstra 알고리즘을 사용할 것 같습니다. Cable Auto Routing 프로젝트에서 dijkstra 알고리즘을 직접 구현하여 문제를 해결할 수도 있겠지만 이미 검증된 라이브러리를 사용하는 편이 훨씬 효율적이고(알고리즘 구현에 시간을 낭비할 필요가 없습니다.) 안전할것 입니다. 이 라이브러리를 Cable Auto Routing에 적용하기 전에 기능 테스트를 위한 데모 프로그램을 작성하기로 하였습니다.  데모 프로그램은 아래와 같은 기능을 가집니다. 최단 거리 검색 노드 생성 에지 생성 노드, 에지 정보를 파일로 저장 및 저장된 파일 읽어오기 Open 툴바를 눌러 노드와 에지 정보가 저장된 Json 파일을 읽습니다. Networkx 툴바를 누르면 Networkx 관련 다이얼로그가 나타납니다. Find 버튼을 눌러 사용자가 입력한 출발 노드에서 종료 노드까지의 최단 경로를 검색합니다. 결과 화면(노란색이 최단 경로) 노드를 생성할때 노드 이름과 3D 좌표를 입력합니다. 사실 Networkx에서는 3D 좌표는 필요없습니다. 데모 프로그램에서 3D로 노드를 표시해주기 위해 3D 좌표를 입력하도록 하였습니다. 에지는 에지를 구성하는 두 노드 이름과 에지의 길이를 입력하도록 하였습니다. 에지의 길이는 에지를 구성하는 두

길 찾기 알고리즘

 Dijkstra는 대표적인 길찾기 알고리즘입니다. Dijkstra 알고리즘을 이용하여 출발 노드에서 종말 노드로 가는 최단 거리(최소 비용)의 경로를 찾을 수 있습니다. 아래와 같은 그래프를 생각해봅시다. 그래프 B에서 출발해 E에 도착하는 경로는 여러개가 있습니다. 여러개의 경로 중에서 최소 비용을 가지는 경로가 우리가 원하는 경로가 될것입니다. 노드에서 노드 사이를 옮겨다닐때 비용이 발생한다고 하면 경로의 비용은 $Cost(path) = \sum\limits_{i=1}^n Cost(edge),\text{ }(edge_1,edge_2...edge_n은\text{ }path에\text{ }속함)$이 됩니다. $edge$가 노드와 노드를 연결하니 $edge$에 비용을 추가하면 아래와 같이 됩니다. cost 추가 그럼 이제 경로를 구성하는 $edge$를 구하면 됩니다. 방문한 노드들을 트리 형식으로 구축하여 경로를 구성하는 $edge$들을 구할 수 있습니다. B-E 경로들 B에서 출발해 E에 도달하는 총 6개의 경로를 구할 수 있습니다. (주의: 진행 중인 경로에 속한 노드를 재방문할 수 없습니다. 그렇지 않으면 무한루프에 빠지게 됩니다.) 경로들에 대한 비용을 계산해 보면, $\begin{eqnarray} B\to A\to D\to C\to E &= 9 \\ B\to A\to D\to E  &= 6 \\ B\to D\to C\to E &= 10 \\ B\to D\to E &= 7 \\ B\to C\to D\to E &= 5 \\ B\to C\to E &= 6 \end{eqnarray}$ $B\to C \to D \to E$가 최소 비용 거리임을 확인 할 수 있습니다. 최소 비용 거리 $Cost(edge)$를 거리로 두면 최단 거리, 시간으로 두면 최소 시간의 경로를 구할 수 있습니다. 그래서 제가 앞에서 최단 거리와 최소 비용를 혼용한 이유이기도 합니다. $Cost(edge)$ 함수를 다항식으로 두면 다양한 조건들

[PyQt5] 화면 디자인

 PyQt5를 이용하여 어플리케이션을 개발할때 화면은 당연히 QtDesigner를 통해 만들게 됩니다. QtDesigner는 PyQt5 패키지를 설치하면 같이 설치됩니다. designer.exe로 검색하시면 찾을 수 있을겁니다. Qt Designer QtDesigner를 통해 만들어진 ui 파일을 프로그램에 적용하는 방법은 먼저 ui 파일을 ui 컴파일러를 통하여 Python 코드로 변환합니다. pyuic5 XXX.ui -o XXX_UI.py 1. Qt Designer에서 다이얼로그 생성 2. ui 파일 3. ui 파일을 통해 자동 생성된 python 코드 그리고 변환한 코드를 Wrapping한 클래스를 만들어 사용하면 됩니다. 이렇게 친절하게 저희 직원에게 소개해줬더니, 아직도 커맨드 명령어를 사용하느냐, 그리고 컴파일러를 이용하여 Python 코드를 만든 후 또 Wrapping 클래스 만들어야 하느냐 C#에 비해 불편하고 복잡하다는 대답을 들었습니다. 맞는 말입니다. C#에 비해 불편합니다. 위 방법에 비해 좀더 간편한 방법이 있긴합니다. ui 파일을 컴파일하여 Python코드를 만들어 사용하지 않고 바로 사용하는 방법입니다. class QSpinnerPane ( QWidget ) : def __init__ ( self ) : import os from PyQt5 import QtWidgets , uic   QWidget. __init__ ( self ) ui_path = os . path . join ( os . path . dirname ( os . path . realpath ( __file__ ) ) , 'UI' , 'Spinner.ui' ) uiClass , qtBaseClass = uic. loadUiType ( ui_path ) self . ui = uiClass ( ) self . u

[VTK] Surface of Revolution

  VTK 에는 내가 원하는 Surface of Revolution API가 없습니다. vtkRotationalExtrusionFilter라는 클래스가 있지만 원하는 결과를 만들어 주지 않았습니다. 그렇게 원하는 API를 찾아봤는데 찾지 못했기 때문에 그 기능을 직접 구현하기로 했습니다. 먼저 회전시킬 곡선(Profile)을 생성합니다. Profile을 회전 축을 기준으로 Resolution만큼 회전시키면서 곡선들을 만들어 냅니다. # create a profile circle = vtk. vtkRegularPolygonSource ( ) circle. SetCenter ( ( pt1 [ 0 ] - self ._origin [ 0 ] , pt1 [ 1 ] - self ._origin [ 1 ] , pt1 [ 2 ] - self ._origin [ 2 ] ) ) circle. SetNormal ( ( pt1 [ 0 ] - center [ 0 ] , pt1 [ 1 ] - center [ 1 ] , pt1 [ 2 ] - center [ 2 ] ) ) circle. SetRadius ( radius ) circle. SetNumberOfSides ( 24 ) circle. Update ( ) circle_points = circle. GetOutput ( ) . GetPoints ( ) # get points from profile del circle   # Setup points and lines points = vtk. vtkPoints ( ) points. Allocate ( ( self ._resolution + 1 ) * circle_points. GetNumberOfPoints ( ) ) faces = vtk. vtkCellArray ( ) faces. SetNumberOfCells ( self ._resolution * circle_points. GetNumberOfPoints ( ) ) lines = vtk. vtkCe

[Jenkins] Python 가상 환경 빌드

Jenkins에서 Python 가상 환경을 빌드하는 방법에 대한 설명입니다. Freestyle 프로젝트 대상으로 설명하겠습니다. Global Properties에서 가상 환경 경로를 설정합니다. Build Step에서 Execute Windows batch command 탭을 추가합니다. 설정한 VirtualEnv를 이용하여 프로젝트의 가상 환경을 생성합니다. 가상 환경을 활성화 시킵니다. $$\text{.\Your Project\.venv\scripts\activate.bat}$$ 패키지 목록을 이용하여 프로젝트에 필요한 패키지들을 설치합니다. 패키지 목록을 만드는 방법은 이전 글에서 확인할 수 있습니다. $$\text{pip install -r .\requirements.txt}$$ PyInstaller를 이용하여 프로젝트를 빌드합니다. pyinstaller 에 대한 자세한 내용은 여기서 확인하시면 됩니다. $$\text{pyinstaller .\App.spec --onedir -p ".\Your Project\.venv\Lib\site-packages\PyQt5" -w --log-level=DEBUG -y}$$ 가상 환경에서 빠져나옵니다. $$\text{deactivate.bat}$$ 가상 환경을 활성화시켜 작업을 수행하고 빠져나오는 부분을 batch command로 작성하려면 각각의 부분을 &으로 연결시켜 하나의 command로 만들어줘야 합니다. 그렇지 않고 각각 하나의 command로 실행시키면 가상 환경 하에서 빌드를 수행할 수 없습니다.(activate.bat로 활성화된 가상 환경이 command가 종료되면 사라집니다.)

[Python] 가상 환경 구축

  Global 파이썬 환경에 모든 프로젝트의 모듈들을 설치하다보면 모듈간의 의존성, 버전 문제가 발생할 수 있습니다. 이러한 문제들을 방지하기 위해 프로젝트별 가상 환경을 구축하는 것이 좋습니다. 아래는 가상 환경을 구축하는 절차입니다.( PyCharm 기준으로 설명합니다.) 가상 환경 구축 프로젝트 폴더에서 Command 창을 열어 아래 명령 실행합니다. .venv는 생성할 폴더 이름입니다. 다른 이름으로 폴더를 생성해도 됩니다. virtualenv .venv virtualenv 패키지가 설치되어 있지 않으면 아래 명령으로 설치합니다. pip install --upgrade virtualenv 새로운 프로젝트 생성 Virtualenv로 프로젝트를 생성하시면 됩니다. 기존 프로젝트 가상 환경 설정 Setting에서 Python Interpreter 탭을 클릭합니다. Interpreter 콤보 박스의 맨 아래 Show All을 클릭합니다. 맨 아래 [+] 버튼을 클릭합니다. Existing Environment 항목을 선택합니다. [OK] 버튼을 눌러 설정을 저장합니다. 가상 환경을 적용하기 위해 PyCharm을 종료하고 재실행 합니다.(Terminal에서 아래와 같이 가상 환경이 적용된것을 확인합니다.) 가상 환경에 설치되어 있는 패키지들의 목록을 파일로 추출하여 패키지를 설치할 수 있습니다.      pip freeze > requirements.txt : 패키지 목록 추출      pip install -r requirements.txt : 목록 파일을 이용하여 패키지 설치 requirements.txt 대신 다른 파일 이름을 사용할 수 있으나 일반적으로 requirements.txt를 사용합니다.