기본 콘텐츠로 건너뛰기

2월, 2021의 게시물 표시

[S3D] 칼럼(Column)간의 최소 거리 구하기

 아래와 같이 두 칼럼과 연결하는 빔이 있을때 두 칼럼간의 최소 거리를 구하는 방법입니다. 두 칼럼의 Origin Point로 거리를 구하게 되면 Cardinal Point에 따라 거리가 달라지게 됩니다. Cardinal Point에 대해서는 이전 글 을 참조하시면 됩니다. 그래서 여기서는 칼럼의 4개의 면을 이용하여 거리를 구하는 방법에 대해 생각해봅시다. 칼럼도 CableWay와 유사하게 8개의 코너 점을 가지고 있습니다. 이 코너 점을 이용하여 4개의 면을 만들 수 있습니다. $(P_0, P_1, P_5, P_4), (P_1, P_2, P_6, P_5), (P_2, P_3, P_7, P_6), (P_3, P_0, P_4, P_7)$ 이 점들로 각각 면들을 구성할 수 있습니다. 면의 방정식은 $ax+by+cz+d=0$입니다. $<a,b,c>$는 면의 법선 벡터로써 면에 존재하는 점들의 벡터 외적으로 구할 수 있습니다. 예를들어 첫번째 면의 법선 벡터는 $\overrightarrow{P_1-P_0}\times\overrightarrow{P_4-P_0}$입니다. 면들을 생성하였으면 빔의 라인과의 법선이 평행한 면들과의 교점 을 구할 수 있습니다.  라인과 법선의 평행 여부는 라인의 단위 벡터와 법선의 단위 벡터를 더하여 그 길이가 0 혹은 2에 근접하는지 여부로써 판별할 수 있습니다. 아마 위 예제에서는 2개의 평행한 면이 있어 2개의 교점을 구할 수 있습니다. S3D에서는 API를 통하여 평면과 곡선의 교점을 구할 수 있습니다. oColCurves1의 Position이 교점이 됩니다. 물론 S3D API를 이용하지 않고 직접 교점을 구할 수 있습니다. 이전 글 을 참조하시면 평면과 직선의 교점을 구할 수 있습니다. 반대편 칼럼에 대해서도 위와 같이 2개의 교점을 구합니다. 이렇게 구한 4개의 교점들에서 가장 짧은 거리를 구할 수 있습니다. 이 거리가 두 칼럼의 최소 거리가 됩니다. min_dist , line = None , None for

[PyQt5] paint() 함수 무한 호출

 2017년부터 PyQt5로 개발하고 있는 어플리케이션이 있습니다. QGraphicsScene/QGraphicsView를 이용하여 그래픽을 화면에 표시하고 있는데 Scene에 들어가는 객체의 수가 적으면 어느정도 성능이 나오는데 일정 수준 이상으로 객체가 늘어가네 되면 성능이 급격히 떨어지는 현상이 발생하였습니다. 이러한 현상을 디버깅하는 도중에 객체나 Scene/View의 변동 사항이 없어도 paint() 함수가 반복적으로 호출하는 현상을 찾았습니다. 그 현상의 원인은 paint() 함수 내부에서 객체를 수정하는 로직때문이었습니다. def paint ( self , painter , option , widget ) : # override paint method color = self . getColor ( ) self . setColor ( color ) # --> 여기서 객체를 수정하고 있음   painter. setPen ( self . pen ( ) ) painter. drawLine ( self . line ( ) )   if self . isSelected ( ) : painter. setOpacity ( 0.7 ) painter. setPen ( QPen ( Qt. black , 1.5 , Qt. DashLine ) ) painter. setBrush ( QEngineeringLineItem. HIGHLIGHT_BRUSH ) painter. drawPolygon ( self . minimum_bounding_box ) 이 부분을 수정하고 나니 전체 프로그램의 성능이 조금 올라갔습니다. paint() 함수는 객체를 화면에 표현만 하도록 해야하고,  내부에 객체를 수정하는 로직을 넣지 않는것이 좋습니다.

[자료 구조] 기능에 맞는 자료 구조 만들기

 이 글은 같이 일하는 동료의 이해를 돕기 위해 작성하였습니다. 주어진 직육면체(PipeWay)를 $N$등분 하였을때 각 Cell을 나타내는 자료구조를 만들어 보도록 하겠습니다. Cell은 4개의 Section으로 이루어져 있습니다. Section의 순서에 주의하세요. 앞 Cell의 3번째 Section을 현재 Cell의 첫번째 Cell로 사용합니다. 그리고 Section은 4개의 정점으로 이루어지고 Section을 공유하는 Cell의 정보를 가지고 있습니다. 이런 형식의 자료를 만들때 자료의 중첩을 피하는 것이 중요합니다. Cell과 Cell 사이에 하나의 Section을 만들어 공유하는 것이 좋습니다. PipeWay가 $N$개의 Cell들로 이루어져 있다면 총 $(N + 1)*4$개의 정점이 존재합니다. 그리고 Section 구성하는 정점의 순서를 아래와 같이 구성합니다. 그러면 Cell($K$번째)을 구성하는 Section과 정점의 구성은 아래와 같이 됩니다. 이제 코드로 Cell정보를 만들어 보도록 하겠습니다. class Section: def __init__ ( pt1 , pt2 , pt3 , pt4 ) : self ._pts = [ ] self ._pts. append ( pt1 ) self ._pts. append ( pt2 ) self ._pts. append ( pt3 ) self ._pts. append ( pt4 )   self ._ref_cells = [ ]   def add_ref ( self , cell ) : """공유 cell을 추가한다""" self ._ref_cells. append ( cell )   class Cell: def __init__ ( section1 , section2 , section3 , section4 ) : # 생성자 self ._sections = [ ] se

[MathJax] 구글 블로그에 수식 넣기

 개발 관련 글을 쓰다보니 아무래도 수학 공식을 인용하는 경우가 많습니다. 이때 MathJax를 이용하면 아름다운 수학 공식을 표현할 수 있습니다. MathJax를 사용하기 이전에는 수학 공식을 Web에서 수학 공식을 작성한 후 이미지를 캡쳐하여 블로그에 포스팅했었는데 MathJax를 사용하고 나서는 블로그에서 바로 작성할 수 있어 글 쓰는 만족도가 전보다 훨씬 높아졌습니다. MathJax를 사용하기 위해서는 블러그의 템플릿의 header안에 아래 내용을 복사 붙여넣기 하시면 됩니다. < script src=' http://cdn.mathjax.org/mathjax/latest/MathJax.js' type='text/javascript ' > MathJax . Hub . Config ({ HTML : [ "input/TeX" , "output/HTML-CSS" ], TeX : {  extensions : [ "AMSmath.js","AMSsymbols.js" ],  equationNumbers : { autoNumber : "AMS" } }, extensions : [ "tex2jax.js" ], jax : [ "input/TeX" , "output/HTML-CSS" ], tex2jax : { inlineMath : [ [ '$' , '$' ], [ "\\(" , "\\)" ] ], displayMath : [ [ '$$' , '$$' ], [ "\\[" , "\\]&qu

[알고리즘] 두 Graph 비교

 브랜치가 포함된 두 배관 라인을 비교하는 방법입니다. 아래와 같이 두 배관 라인이 존재할때 바뀐 부분을 찾는 로직입니다. 이 두 라인을 비교하기 위해 문자열 diff 알고리즘을 적용할 수 있습니다. 먼저 라인을 구성하는 항목들을 코드화 시킵니다. 두 라인을 문자열로 표현하면 $OLD = PRPTPRPBPOPRP$ $NEW = PRPRPBPOPRPTP$ 가 됩니다. 이제 배관 라인을 두 문자열로 만들었으니 문자열 diff 알고리즘을 적용시킬 차례입니다. LCS(Longest Common Subsequence)를 이용하여 두 문자열을 비교합니다. LCS는 다음 식으로 표현됩니다. $$LCS(X_i,Y_j) = \begin{eqnarray} \left\{ \begin{aligned} &0 && if\text{ }i = 0\text{ }or\text{ }j=0\\ &LCS(X_{i-1},Y_{j-1})+1 && if\text{ }X_i=Y_j\\&max(LCS(X_i,Y_{j-1}),LCS(X_{i-1},Y_j)) && if\text{ }X_i \neq Y_j \end{aligned} \right. \end{eqnarray}$$ $X,Y$는 비교할 문자열이고 $i,j$는 문자열의 인덱스입니다. 위 식을 이해하기 쉽게 2차원 배열로 나타내면 아래와 같습니다. 가장 큰 숫자인 11이 공통 부분 수열의 길이가 됩니다. 1에서 11까지 비용($Cost\text{ }=\text{ }i+j$)이 가장 작은 위치의 숫자를 선택하여 그 위치의 문자를 연결하면 공통 부분 수열이 됩니다.(공통 부분 수열은 하나가 아니라 여러개가 나타날 수 있습니다.) 공통 부분 수열은 $PRP\text{ }RPBPOPRP$(동그라미 친 부분)이며 변경이 일어나지 않은 부분입니다. 해석을 해보면 $OLD$에서 가운데 $TP$가 삭제되고 $NEW$에서 맨 끝에 $TP$가 추가되었습니다. 두 라인에서 브랜치가 변동이 없는 경우에는 두 브랜치