
    ]j'                        d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZmZ ddlmZ ddl m!Z! ddl"m#Z# ddl$m%Z% dZ&dZ'd Z(d Z)d Z*d Z+d Z,d Z-d*dZ.e/dk(  rddl0Z0 e0jb                  d      Z2e2jg                  dd !       e2jg                  d"d#!       e2jg                  d$e4dd%&       e2jg                  d'e4dd(&       e2jk                         Z6 e.e6jn                  e6jp                  e6jr                  e6jt                  )       yy)+u  convert_parts.py - STEP 어셈블리를 '부품 단위'로 분리해 OBJ로 뽑는다 [CEO 2026-07-19]

통짜 변환(step_to_web.py)과 달리, STEP 안의 조립 구조(XCAF 최상위 컴포넌트)를
부품 하나하나로 나눠 p001.obj, p002.obj ... 로 저장한다.
각 부품 정점은 '조립 상태의 절대 좌표(world/mm)'로 저장되므로,
웹에서 모든 부품을 원점(0,0,0)에 그대로 로드하면 원래 조립 모양이 복원된다.

- 부품 이름: STEP 라벨에 실제 이름이 있으면 그걸, 없으면(NAUO 자동명 등) 순번(pNNN).
- offset_mm: 컴포넌트 배치 이동량(참고용). 좌표는 이미 world에 구워져 있으므로 웹은 재적용 금지.
- 좌표 규약: 기존 fab_models 131개 OBJ와 동일 — 네이티브 STEP mm, 축 변환 없음.

사용: python convert_parts.py "입력.stp" 모델명
      -> fab_models/parts/<모델명>/p001.obj ... + parts.json
    N)STEPCAFControl_Reader)TDocStd_Document)XCAFApp_Application)XCAFDoc_DocumentTool)TCollection_ExtendedString)TDF_LabelSequence	TDF_Label)TDataStd_Name)BRepMesh_IncrementalMesh)	BRep_Tool)TopExp_Explorer)TopAbs_FACETopAbs_REVERSED)TopoDS)Bnd_Box)
BRepBndLib)gp_Trsfg      ?g333333?c                     t               }| j                  t        j                         |      r	 |j                         j	                         S y # t
        $ r Y y w xY w)N)r
   FindAttributeGetID_sGetToExtString	Exception)labelns     >   E:\도진팩토리\3D스캔및티칭시스템\convert_parts.py
label_namer   &   sU    A=002A6	557&&((   		s   A 	AAc                     | sy| j                         }|syt        j                  d|      ry	 |j                  d       y# t        $ r Y yw xY w)uK   실제 부품명인지, STEP 자동 생성명(NAUO123)/공백인지 판별.FzNAUO\d+asciiT)stripre	fullmatchencoder   )namess     r   is_real_namer&   0   sT    

A	||J"	   s   A 	AAc                 "   t        j                         }t        t        d            }|j	                  t        d      |       t               }|j                  d       |j                  |       st        d| z         |j                  |       |S )Ndz	MDTV-XCAFTu   STEP 읽기 실패: )
r   GetApplication_sr   r   NewDocumentr   SetNameModeReadFileRuntimeErrorTransfer)pathappdocreaders       r   load_docr3   A   sx    

.
.
0C
5c:
;COO.{;SA"$F
t??4 1D899
OOCJ    c                 $   ddl m} t        | t        dt        d       g }g }t        | t              }|j                         rt        j                  |j                               } |       }t        j                  ||      }	|	|j                          d|j                         }
t        |      }|	j!                         }t#        d|dz         D ]b  }|	j%                  |      }|j'                  |
      }|j)                  |j+                         |j-                         |j/                         f       d |j1                         t2        k(  }|	j5                         }t#        d|dz         D ]J  }|	j7                  |      }|j9                         \  }}}|r||}}|j)                  ||z   ||z   ||z   f       L |j                          |j                         r|r|sy|D cg c]  }|d   	 }}|D cg c]  }|d   	 }}|D cg c]  }|d   	 }}t;        t=        |      t?        |      z
  d      t;        t=        |      t?        |      z
  d      t;        t=        |      t?        |      z
  d      g}tA        |d	d
      5 }|jC                  d|z         |D ]  }|jC                  d|z          |D ]  \  }}}|jC                  d|||fz          	 ddd       t        |      t        |      |fS c c}w c c}w c c}w # 1 sw Y   /xY w)uX   shape(월드 좌표)를 삼각분할해 OBJ로 저장. (정점수, 면수, bbox) 반환.r   )TopLoc_LocationFTN   )r   r   N   wutf-8encodingz7# convert_parts.py  model=%s  coords=world_absolute mm
zv %.6f %.6f %.6f
zf %d %d %d
)"
OCP.TopLocr6   r   LIN_DEFLECTIONANG_DEFLECTIONr   r   Morer   Face_sCurrentr   Triangulation_sNextTransformationlenNbNodesrangeNodeTransformedappendXYZOrientationr   NbTrianglesTriangler   roundmaxminopenwrite)shapeobj_path
model_namer6   vertsfacesexpfaceltritrsfbaser   ipreversed_facenttabcvxsyszsbboxfs                              r   mesh_to_objrp   M   s   *UNE>4PEE
%
-C
((*}}S[[]+''a0;HHJ!5zKKMq!a%AAd#ALL!##%./ ! ))+>__q"q&!AQAeegGAq!!1LL$(D1HdQh78 " 	
- ((*0  	1!A$B	E%:EqadE%:PUAVPU1!A$PUBAV#b'CG#Q's2wR/@!)DeCPRGVYZ\V]L]_`FabD	hg	.!	JZWXAGG(1,- GAq!GGNaAY./ 	 
/ u:s5z4'' 
%:AV 
/	.s   #K75K<LALLc                     | j                         j                         }|j                         }t        |j	                         d      t        |j                         d      t        |j                         d      gS )u1   shape TopLoc의 이동량(mm) 반환 (참고용).r7   )LocationrE   TranslationPartrR   rL   rM   rN   )rW   r`   rf   s      r   component_offsetrt   |   sV    >>**,DA!##%OU1335!_eACCE1o>>r4   c                     t        |      }t        |      s4t               }| j                  ||      rt        |      }t        |      r|}t        |      r%t	        j
                  dd|j                               S y)u_   comp 라벨 -> 참조(원형) 라벨 순으로 실제 부품명 탐색. (이름or None) 반환.z:\d+$ N)r   r&   r	   GetReferredShape_sr!   subr    )stcompnmrefrns        r   _label_realnamer~      se    	D	Bk  s+CBBBvvhBHHJ//r4   r7   c                    !" t        j                          }t        j                  j                  dd      !t        j                  !d       t        dt        j                  j                  |       z         t        |       }t        j                  |j                               }t               }|j                  |       |j                         dk(  rt        d      |j                  d      }t               }	|j!                  ||	       |	j                         }
|
dk(  r|g}n+t#        d|
dz         D cg c]  }|	j                  |       }}t        d	t%        |      |fz         g "g  d
di !"fd}t'        |d      D ]:  \  }}|j)                  |      }d}t               }|dk\  r>t+               }|j-                  ||      r"|j!                  ||       |j                         }t/        ||      }|r|nd|z  }|dk\  r|dk\  r|j1                         }t#        d|dz         D ]h  }|j                  |      }|j)                  |      j3                  |      }t/        ||      }|r |||d|d|       S ||d||fz  dd|||fz         j t/        ||      }|r |||d|       - ||d|z  d|       = |'t%        "      |kD  rt        dt%        "      |fz         | ddt%        "      "d} r |d<   t5        t        j                  j                  !d      dd      5 }t7        j8                  ||dd       d d d        t;        !fd "D              d!z  d!z  }t        d"t%        "      |t%               t        j                          |z
  !fz         |S c c}w # 1 sw Y   dxY w)#N
fab_modelspartsT)exist_oku   [로드] %sr   u   최상위 shape 없음r7   u*   [구조] 최상위 부품 %d개 (depth=%d)r   c                    dxx   dz  cc<   d   }d|z  }	 t        |       }t        | t        j                  j	                  |            \  }}}	|dk(  r%j                  |dd       t        d||fz         y j                  |||||||	|d       t        d	|||||	fz         y # t        $ rG}
j                  |t        |
      d d
 d       t        d||t        |
      d d fz         Y d }
~
y d }
~
ww xY w)Nr   r7   z	p%03d.objr   z
empty mesh)filereasonu     [%3d] %-28s SKIP(빈 메쉬))r   r$   	name_fromr/   rZ   r[   bbox_mm	offset_mmz#  [%3d] %-28s v=%-6d f=%-6d bbox=%sx   z  [%3d] %s ERROR: %sd   )	rt   rp   osr/   joinrK   printr   str)rW   r$   r   r/   idxpfileoffsetnvnfrn   ecounterfailsrY   out_dirr   s              r   emitzconvert.<locals>.emit   s   clc!	G%e,F&ubggll7E.JJWLBDQwe|DE6#tDELLt)TbT  73b"d:SST 	GLL%3q6$3<@A(CAt+EEFF	Gs   A#B, >-B, ,	C<5=C77C<)startr8   zG%02dstep/zp%03d_%dseqz%s/G%02d-%dzp%03du<   [경고] 방출 부품 %d개 > max_parts %d (중단 안 함)world_absolutemm)modelsrccoordsunit
part_countr   failuresz
parts.jsonr9   r:   r;   F)ensure_asciiindentc              3      K   | ]D  }t         j                  j                  t         j                  j                  |d                 F yw)r   N)r   r/   getsizer   ).0rc   r   s     r   	<genexpr>zconvert.<locals>.<genexpr>   s2     REqgqy!ABEs   A
Ai   u6   [완료] %d부품, %.1fMB, 실패 %d개, %.0f초 -> %s)timer   r/   r   makedirsr   basenamer3   r   ShapeTool_sMainr   GetFreeShapesLengthr-   ValueGetComponents_srH   rF   	enumerate
GetShape_sr	   rw   r~   rr   MovedrU   jsondumpsum)#r   rY   depth	max_partst0r1   ry   freerootcompstotalcomp_labelsrb   r   tidxrz   	top_shapen_subsubcompsr|   top_realtop_pathlocsidxsubcomp	sub_worldsnamer{   manifestro   tot_mbr   r   r   r   s#    `                             @@@@r   convertr      s   	Bggll<*=GKK$'	-"''**3/
/0
3-C		)	)#((*	5BDT{{}344::a=DEtU#LLNEzf/4Q	/BC/B!u{{1~/BC	
6#k:JE9R
RSEEAhGG G(  155
dMM$'	 $&A:+C$$T3/""31 ) #2t,'8gnA:%1*$$&Ca+"...MM'288=	'G4E6h3NOJ$$=u&(D$)??A , !T*BYFH5Y$x@G 6J Ui!7LPSTYPZ\eOffg "%jH $	bggll7L13	IQ		(AE!< 
J RERRUYY\``F	
Bu:vs5z499;+;W
EF GOg DZ 
J	Is   ;N08N55N>__main__u0   STEP 어셈블리를 부품 단위 OBJ로 분리)descriptionr   u   입력 STEP 경로)helprY   u   출력 모델(폴더)명z--depthuK   1=최상위 컴포넌트 단위(기본), 2=하위 컴포넌트까지 분할)typedefaultr   z--max-partsu.   방출 부품 수 상한(초과 시 경고만))r   r   )r7   N);__doc__sysr   r!   r   r   OCP.STEPCAFControlr   OCP.TDocStdr   OCP.XCAFAppr   OCP.XCAFDocr   OCP.TCollectionr   OCP.TDFr   r	   OCP.TDataStdr
   OCP.BRepMeshr   OCP.BRepr   
OCP.TopExpr   
OCP.TopAbsr   r   
OCP.TopoDSr   OCP.Bndr   OCP.BRepBndLibr   OCP.gpr   r>   r?   r   r&   r3   rp   rt   r~   r   __name__argparseArgumentParserapadd_argumentint
parse_argsrg   r   rY   r   r    r4   r   <module>r      s      4 ( + , 6 0 & 1  & 3   % "	,(^?gT z	 	 	 F
HBOOE 4O5OOL'AOBOOICf  hOOMTI  K
AAEE1<<qww!++F r4   