From 4e8ba476f5fae7a019f53aa68efd1e411b5a99de Mon Sep 17 00:00:00 2001 From: hesuicong Date: Thu, 11 Dec 2025 16:28:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/compute_print_net.cpython-312.pyc | Bin 31795 -> 31530 bytes __pycache__/config.cpython-312.pyc | Bin 783 -> 17796 bytes __pycache__/download_print.cpython-312.pyc | Bin 63168 -> 63096 bytes __pycache__/general.cpython-312.pyc | Bin 12690 -> 12612 bytes .../point_cloud_layout.cpython-312.pyc | Bin 48598 -> 33720 bytes ...ctory_type_setting_obj_run.cpython-312.pyc | Bin 9228 -> 9107 bytes __pycache__/test_load_json.cpython-312.pyc | Bin 17109 -> 16956 bytes compute_print_net.py | 8 +- config.py | 1 + download_print.py | 12 +- download_print_out.py | 4 +- general.py | 2 +- point_cloud_layout.py | 369 +----------------- print_factory_type_setting_obj_run.py | 25 +- test_load_json.py | 5 +- 15 files changed, 18 insertions(+), 408 deletions(-) diff --git a/__pycache__/compute_print_net.cpython-312.pyc b/__pycache__/compute_print_net.cpython-312.pyc index 9c4e1e4b4b2a0f51e5cfc58a6358c7b26d759d11..87fda832a556830cb80b484d02ca23bc5bfc7203 100644 GIT binary patch delta 2700 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≫o2U=fgLdCb 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 AC;$Ke delta 10558 zcmbVS3wTu3wLWKFc}x=W1OfrVD=+~GZxu)|ydOFtfEA-KOy-<~36q&|p9vu`FsP{b z0%7~W7nKl=YA+S}IK-p^0z^{TfT+hQ+Ai_)TqPqbQvdiAbHviIK*NMG3L)F5)6Sf!UG!Lp9q$--Cxk5S3 zxpld!VpH_=rZIAKS;3IBMny~ifqKM-o{CbJpVF2=ZJ8-;1u1QAYRgJ#E9`A6j8jui zN>dRv<3En)a7b}wITiJ3bdhGalg7D+Ayk%_Z?)o zR-%pQM>tX&bpgX-t@MI{M{8p)pc|`=OXau8T{oDN8KMtfr0h`wwnil@U|XN7DBF|{ zrD>|X?mr9V>$ald#eIoVXlC}%%pIKAd|HrmTR+!X03gEyi}XVeJTm3 zX;p;NwfTfIG)=pVdd<{yt(yMM(iTu{iMEh%gtmxqHcPaaBwEtzE*Fzs)zBq%m+;?9 z>n`QLbL!@3OR2|P`dSv6*IZdwsVaIU`qyg9cPiS79tKA&Z&2#0w3XChzIHj`WfrM+ z1yOo0psdzvh-QJdig2M;yHhTAj2QwcR@AN3FW0WxJW)}^TQuA5rU&K0qG22ByxO|D z8tv-3ReFt>kfCUGb+vl-LyC5dUi*;ph`nw#e_i{Ct!|CJO1P=(61`Smt*_~sc2#Lp zsdmwYwYs;dljCT;ex;Ed})#w(N5j%5RI(=hR(A zvUq6JW%^Z3Kj@yOls{B^DCM)eNVSJa>4N#)VWukKiNs^+Kf^=La*a_NC~H}u{P6b zL_@(CO~vk^i{><;!4V3Eb<-J$w1i_s71EmxF#?@O0!9%SSp=z*C8}gr@lfY2R2@lp zieGcYCsWZ>YuSL7DIv+3NvKY*rIfH>!2+$*CN@)r2mvhl&~6I`y&ji{p%zM6#@dP| zr|^dxbuo|1o@_He>W^*m#Uj4uh^B{p4Z)Bupn|w@5HJqd0k}c#8adIOqJ;eSk<-*F z`O(N>&U=X9fkf7*!}cNpTDY7r06cf&VFnlrc3-KUP{wnd>Shurc7rMhJ;x+)3JaZX=nGqyo;1?8kqJ@<%mu}DK zIJ*sNUPvuH>as<4gS1ya)1~PFGJ%;Dh=f|2!#*SC4{YwQHO~or+mkEy&>zzkL|9mw zn>pc@W}na-eO9HZ`s5MMI5&b*3ast@$o!P9GcmPct zNp9%WW8Y5cuNX3$&6)uU<^?4z(=u1KFMKO$Un-GSPgRsSe@XSu#H$r?Rh=Mfrj-|Q z8pvpf1pJ}t4UsM4St2NtyQfWbAeZcm3{G@S^V#F~(T5nwwad}KJ~5L@UMozK_Rjui z-TN^3!qDV0+m*}d<8jnrN<`AZkKi-Ky!d<)kTfwSIdvvzBF@hm<~&N(<%#0is~y8& zUQ;b&ul3li*kuo%Aa9;Cdnr8FYpP2;s_BdfO&3JN%^c)V)#J9bX`4Gc7tw0zU>Qv=n>)4uiO5JN=n<1d+&*`>^Eg#qkvK3npibA&PX`1E z3?~5-Te+U23D7_*nuvm|Ix(Yiv#pMEMk7_2jwnTw>1-9jnC@{}GK4`H0yKlf$xA># z=4LK;NnImOqKEuy{yMcfQG3}FHs3&g!7jkTpGQ?eOMYK02 z)-Bv`kAGJXihl*uI{}u_P~w3TC?OEM;-@J78-X!EU{JML&}Z!WVAwC(#A#}Il%~|k zQk6txX^yHsli0rOeO3L746fL>mNPX+_gi4$q?x5v4x519oSTwzO-EBC7&h%jOn5S^ z_~zX0TDG`)(c;yXMLK28%Hl%!w}JTXaR@|j$I7A3&#CVD!~-kuv&G+~qG@Zbw^A|B z0rd5Hx)&5S5=;0IDDxLqU$J;~weO0WMT;-@dFf+~B_yX2mN}V5@~H#5YJ(t=LrVbCt%E6Yq8?w zZ~4mCRDBxO>LA#moXt{la(66wGb4LfV*T2vs{SVN%9Sq1fPVBa273gM^dpw98g<_X zJPJ4nK;{=eB

)#38Emf&live<$nP+*NwQl*sSj~To9!~L;| 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 delta 334 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 L^~6i+xH|s=(C=D8 delta 15870 zcmch84R~9{mFT_ty^?H8*1u%gmi#Zrj_ufS9OplA5+@EJKuC#6DimMEu_OO*CC5K| zRk_eEc5!jcr5gtVaoDyvTUxT4zTLc13f+3sb zuCAmw6!`kS@7+Y_o;!19=FFLyGv}NcUwTOP+8<>VKhMtAQt-UcxVG#0cPZ+(_#r*& zB=X*4@~MjZqsAUnyNRY)id6=UJ?3^Z!Bv1;+ARcE2dzD}b{oMN!0qjJf@=VGv^xl{ z1>D*0B)AUny!JeTX94bNcLARLC8|B2%>h`z>H!wA27vA;n+q?GhGLEIE@Djpi&-BU=VvYseSfPP66k)x=f+TuaiL+nZT0 ze61s2vUV9;3192k6#zG|RRA}#)c`jIHnS^_S5Or9CR@|KC9vfQimiQ8*1nZsb%1RP zNCT99cYigFOITprw`dU$8D$iddqm1fztc1{MVhOJHd+bR0N5^q ztp;p&Ao|%GZe7;!-=!Orut16i8vaZ*N3m#N*RrZVu1_hN&ssI9dH1K(T#e1Ah-eB# zx+c&X*cI3sXn}MkRzguUHB1lF-bUd*+HJG;b6ve*-%)=@xQ`q5g@^kCKDLV!BJ?`b zc{-u*41|3>eQY2Y@*O$SHzZu5JuS?>pg(-HkL&T$2_4%v*y{`Vd-{U`_%w9%_4E&f z1BBS;4+bI4ygbbp7z$tfH`=9eGQdtk(bpg7O(+Msu5cisB>ZqWf?M^!={tpd^;&uD z$~xhIx=Psq#Ey#(sqd6Axqy0QoKtvHlt{ zq3j3-{G3IX zqXp)Rb(Uka?hg3o?nJO(__}qgi&UB#WVS05_V;!KI36gzEy!&9RLKf{c*sl_2v^*t z!XFR044_@8FUwf^sLTO`5t;C&zmTQ{`iM)HktLk*m1tyQLYNYsv$(Wk0bV|$6@KBf z35V`8MR|p^2=0O@u$KtetK!m(-UUr0n?rHPcywiE{ zoYJTLv$-?uT+WKoou80(HCsP&-_=&nU)DJ6`Bn!49d4~dcyOUr7Yh|=J7j! zhUXShb6k&`MSSY^soQ6^ z&eHKu6EMmXA3XxJEr@`xP{Nz;5}3*Bg}aXw3NQI=QE3iF6ug3$9S0F|`mhd5;ls*! z#S_$%GKmT_t~`v3jXXTX)4c3k$+ln?D@6GYltT8^OOLqkalie-cdt!OU4QKC^`|eq z_2^R`_giO1uU(jY`(K{C{>|r`-TU{QS1+6KMDS!Rh>A&=I*tW8j{7?Lg2Ap(S6?sD z#ma=Xzk>yRexj?lGogdU8}91xK~hM#!)b{ovc(*qxH=Nno~~XPGI5Rj!hOCWA1R98R*y`J2IeX`s+ZXhv ztGa^ek=b>zHM`@wwwR(#TqDvDLc{-GM-W$0Y6K5{>Ig7%#{3c!K?ln!P5dLeIaQ3`R7`J0oa{U>vDGkIIsNJ z$x5XX@^jRlH$#h6tCZZ#-7XgC)(8{dPB` zoyXnR2uOlHLL?i)i9z(b(C;)W&%oF3g>mN_u$y+~{nCXkaF|H;GOs#wTm-+XMYIDR zMgj!y3opAuP_!xk8!gzJ$O3l!ykdE0PhhG%-OmKm5$FwbeO+t@Po4s+>YG5AMGG4W zHjpj;^9=6n(2YBUHw(;J-+&KkYZvH3x9T4t!9)w?h50~!h;;~`FZ^x`5day0<$EGG z3-sK11Sl$~Hzj#)7!op=IpD6)J_||UCQ+fa!j=NNFyk%-v*#!7S7`bpK`5%RlZGD0 zifHaQ7Ef9oLZWC}%L**WK+L{=l&clB}M0P8;56%2%$y;+1Dq2p zAXd*o#O_5vrsqDaMD6D~iHwNthN~;N9EHE#zLNF}|7ClL>OM@Z6MnP3jebN}v7?-R zQMhZzKQ(zVc^v|r9|;*dM8-FvVLAVx56%6AybBV8NYa6z3PGQ+Vdo|=h9f&;V7=~b zJquF)eQUAS08?QVPKUEX-mW+39N`bU#`D)u#=;vLsa)&mt_8L6zRN2Z{?Ut7+=4#vKO50+LW4a>F6u&#Hr7SD{u%xC;%^@^| z-jQeV4HtiKsGW|IiAhGM4KntF6T@%;a9Fxd1YlX;Y$v956qk}BxCdlmB(PU+ARyv< z8!gkunB5WEj*A+)?fuZRyT5pa78l2X0}VgP01%Spi6VEFueOKCa^@YLQRl z0x0H@7J&sChbr!0;#7iUhzLlx204*n5I92Jnp|EK>M8gDT4oj z;1>wc$LCG~$lPhT7^Wg9QV1_}JmnHS82FYR)MKR4esEmeHF{ZxtP@niVJo^ayi6$c z6x(D|vI)C_ipU{F-a;MzPsme%6XQAJ0z=y(yyVf@Bg(Xe$SWn=FrpG(94wTpx(&jg z4&`~&VcfLH(`}Zp^t;<8T+upda+Ncb;%A-&C)b8sQmVjzq%BBAPI|q2%Ei zUL$z`5p9ZNE7-N&9>L==TUoi3FTEqWuu-`8xJ%Yuip@lIyjIGhsc?C!Y8|Ucr%!wY z+Ekg;rZUi`s)Xn&bGS^o?*JIUu3x?M@H-DZ^w!M3T>H+%TVp4GJNm$0uX*|AkcUOc zwg7_@P3l2ZbqVG~fCHm85aJFX&F3&pyAQp+ZNa_)HlZE}cLlq`!-<>&0StN6v*KG^ zH)x}yFVi3NgID{H_?1iI5~4}$>%cn1eJ3kn9Sn4J9t-=TN4ti6eFI@%-;r*Nouo3P zbliO)oh!rovXKh+CLb8)VNrKhLk71JK><#CqIb0?`i25QU#M#&kkF&mj+$4Dv#^OQ ztP-2VJue!{f})ZJ!nf$Vnn`GeFy#;Xad|^2t`b_Ph7}h2Ur=b2x>{87Oy#-CSoOB~ zqL#S%&|Fc=^wyuseyRJJF1Ek@)z*6-y)#zSGJ0FgeCXF^<04vYc8owdU0Nsr4~; z)XpxoUDv-xD{LA!a$%Jk>mX>~cZPTtQ9TR10Ntawmi1!OJIZTI8eN0J`r^00=(TD&r*IE?;=gR((4zXz<9u<0V^rD-_`V9>LcE zq%9=)NZ2I{RkG&5+3M+sI2u<4BHoB1_M+lE`KNJv`1y=_h z-i93xVu!0z9e&(DlRM2$@o|00XzRzP3P1e3tp!3IP2)``@0lK&wZzOdab0apQ7cL) zrM7_0lp>yo34e+TL#yJ$E4$^W7oCzoA~{`DM9=F$6N9RV74Fkgj6db_242tS zu7|=C`3j29X`xOk4?hir#;^+GPQtLqT)@kCqolqfCSJ)K(Jqacd3nT=CUf$Z3^FU9 z&07hXjW-H!9)<{uoTXB=*m=F+sk21wX~h_EgmaU@>35v_kD5uJ-b>i7H?*@oOq#BGk(-}?(P^2^RcpdK`opOaO z!rmO0g12?sakrA^hn>RDn_(k>xr*CTK^VX~sq!y-9JsGJ5snjCg!@Eadu}6x-xE{#U4coQ&2>0b^6}(yO!is!8pD!MP zoJh%MVcu3EHN%^IsCUT&gI(yc*i!p#a-x;;ibz>ftaHaLwlb+~$P!jdWxFd;DA}nz z*g{j2(8!l%%&u}^x|#?YXwrhljbqusZCWkK76MDkJm+}=SzyX}NX$qC`71Csmcr&`&h*cMRr3z?j zVxNLy6_HB#t;nDgS+GcSz``zIp^C2(S;$MVz^Qa<_|?O&;S*&F=A1OT6|Ms? z6gaQQu(AvkXCkX63P4$}3dsO(;Omp`hLG%}V&c2$navvbN`6Jg%!Y||9K3Jtc)E8u zGL;hlk=5x8>~=b%@s@6{<{PDAAl_x&&Rd=CHorn_6}VGQG^HEBmRCqsMAoD;u;p|{ z(=9Ep;hUslx3(NzlWrN>`vy#l)ULwjX6zb_M0ay|nrD(br7@$NF+BmG#qH9JiCTCK zJId>p#(F~3&uWJ`)oqy+qWN%D~+PTALhFJ zy`5mD51a@Mpo?|=ndh&4W%k<1N1&psFFtwg+tZ?l@{wX?PdEs$yT21)e{U!8r zaB3QWqfp5GKzOdN$oT;}bk)`GJa+o+=bn3O_T06n9=>++tN-!+$3GB$*H=V;AmsEr zqQwaXItB>^97l%4D31+_acP&Q2>O_iLzIX(A{@qhGQt|fgGl*4TpI$KAlTd|@p{JElboBKPbGY{=l%ZfC(4Wu`^s^AaBHj`q&F$yDld3BTyKMPEbOEVip7 zEX@b57>UYIGTG={p<@MggEiklXhk0m=wVovIc*7ZAJ^5{)$0$E9`tvCuLfSA9w(fb zLuyNCQwh;t(Sz&t_kaoA#d2thib>dAY&&7V7Ny>h&}IvAh{G$M;)qozbm$u<=@MBf zUj*ub!}w&JIhC&$Q=-6PvN#AKM7-S|piP)0@7WjT{JkNFn!&nIBj3Pf!X~kkOppYe zFpvb4n8b)2COu58a8T?F!LYXp1}D}Af@+8tOi%@x2)ayZF~FEu*n9*%jbt9QB;3JU z1S4Oh{v19SP^$r$IUB8 zZ(A^1M{j$N5pHmmQOGY^?%ZBt;-)QQ%ncP~_RQGlOqB$8uZ}UUMW^d*`RVerRi~?B zD@M;u#hh`)m|AkqyDt~i z%@x$eO{;(laK#Trrw(1VRLxnc;+B=*g1ZXOww-Rf>}s5IVb!k9W7>Jf0ll&n&os?h ztHyL!Z8aCvaa$9%S%0A|p0^nqT6C_u0FL`c5CwF+YR=X;mL)Zr84p+}D4TW76@Vb~ zl1)X$w(uy=)i%aJV^@sMQ!A%d&Y0uIvYGqmjNUQz6;DHqDNMSNlV2GBLabo*yuNWU zD`&E8ye($0na`@dNdlWB2`n&%%S`zkQ$EjlAz&cJ3yS8LqF8bLg?nOm+#QWEMf1!7 zB7Hb=Y`(N-p4oLnONoOmhBTnH*G!FL+ZOc3Nq(H4-tnrwc){eH?s#mK zsu<6}5tu&ys=g8ySFY`pZb~=ZHp{$fsJoFzSysRAqBPl)+Hvj4x{2(oney5El09Cy z8x|3i)7WG7s@Ten7__JwlLvWIGP^RvZle+*+Wy7F2o8Qf%4EYODNB-FE z*Y)-VeE(Kw!KGFO(FkTft8M`np!q9bSScch!`$JTt&lg#jifOQRFd!|eooOU7h`4%0%dtiZ_1F3YiCQIbVz?1pJW0i~tPZ$l1b1`*33c@*)NG)W3j{)NoO2tL2is#P z>f`(@Aj8S`Nb(K0dIFs!U3`z=cL?ovjFZA6hDvkbMj8j9J0CImLfj~9A6Ievhyo=9 zN#U+e`*`~_Jza8I4F{SH=WOw8FWk|A^n>FEAw3EiCx_zMZZYxWh+(?JMIp>l#j^|3 zbRYM1MVEWBak??4^TZUMTaQ6fSAP<~9tngA<9WGJmL^v;b>XCKju%m+(R=`g8HmZl zr^lFP<%fVeB7>6)1y6xl4snHu5@KIUak`8^NF2{J;G0YY@(sa9_$CvAd@Ckg;BmlA z@EpmH%6NK2hu4XcZ-w|yM=K*LUWJom;77oX76;+&@0`2-om1Cen0jmG`)_}D_S%aN zl4Js|O+N6}*Df>@9BCyBrP=+?xqrU?z<<5=Y9s-i<&wuGZzCUp-`oh!Kzw*yl zFMY3KZgutpDa} z-1iPuxreLVBZo;nBvwp(1Yju&Jc*56z4X<$AAA8gxqkk8UitN;@G0&AG*Adg2Wu6? zj8A9};Pu1p9M{K*CVe}Uf@w~S`Wz?+M)W|BZ%BN>bw&IdNvK4Fo%;q*M{k|o#3)9) z9&Pqyc$XW4+TO(}4zUchkjxaL8*6$nGEM(V?O4pQ#~k(X9N2Ihu0ne2!`Y)-t~kr* z6t>arW2;Y=zNey0d1I;tCi|pynx1mZG0rh1`Py;wXYy-Yc>~UB^WFz-Yn&V!ADXtz z^5PfP^jrruXd-S zpsa>9Nqn|Ylb+;JCiuw_PZ<47^6+lnBi!C?TkXjxX=2Tvy(H}se%a;7`;_{DDzE!f zsnniMMWl$&5qi4wZnLsBzNm|4?PpS|4FcZ=2(!==8$MQ=IiD;|O-h==huymz8o1f+ zfSY@`0l^a~28m?_^^p?c;PFCR4(nvIpUsnkACXcS#g}xK;B2UIvH5&yIwvDCUCb7+ zh0nUBywy~sj4u-)AWWAE*N>Oe#ezP#U2jb*XttQQ3V#k-lqOKLRv{AHE>BNR+f9_E zB4q#%D6CQoFc%;o!ZRF9aOh9jld?#8xJK*|XyzQ=1VO*#CIQmZ-zD90@eF<&mxDSJDtg;XZ;rf>Xw2TU0U)0AJ|%p;x1sQM;N~4X z(0K=g=-iXALbz!FU<`D_OZZdoKSsHAn7SUp1_T=cj4(BR4t&$jfY&a@KX5bWHevo| z1h9Wm9EM1^EeKF|a2Fvhp*{-kY#48vlj88g2#|QPoDQzw#e#&QJJi>kIHqI=dip~N zd1&AWmygvJARzIUJxFFGPBLDEhYRpmJA4ClH(xEcdV7L1GcyeoQ!kpaToujaU_82N5G-1BkqY27+PkX-xTh z1Ybw+4Fum5etv(xOi2s>cE7WlbdABT`QYGLyeVbv@Wbs*@!EeV5DvrTB@AAKD!`#$ zA>-th@D&og&l z(dESyd4E_62_Fp<74K20Uaqq4(J?VKW?(_v1I*BZrtOPzv5KxDOPvL1wU)P zc-M(PE27+L{V#M2kYTCu<`1UmqD4Wjt=VFHm{ zFJc`Cjv?qmfK!g^1K{EM5xpM)Mi)2^0p4xnaCveAv~XtBZm7p0z&Sv`Q_73~%KwDk zc+1U^l$|yN9lGhj68-{6QZb+o;Luq-z^sVK;Yv3AlQQ9Njbsr8{FG6=RsyHDXgzg< zrB&flaTY+;f~V1Jm3A1>D2ph1O9@qOz??b`;LH;B=4I4MUIF3uJ9s5Pm~g!`8gtM? z^6>P>vsw~SFK4xriZD?%u(=i3)bJXSO*5xv)p#G8WuBBrG`%*G0i30_MNY+Pc%?+Z zGk9aBS|c$D%B*D>L#gW~N!jV}Xe647ZKKd`7UHn1n5u&lszDh|9i(XZH}OYNjop&| z<&RGvmL=7mj%c+6&FxL7AifpsI>KRCm$RU&kWdYT0+2eQ7PH(O9@vhk#Z))uL4ew}Lt zt5j#Wtjn9zq-}_`P0Xz56>3G%+=%D7qX3)v-9V&^QVu^XP0B%vFY|1E@rMA z+rGf$US{lbjD6bjDpPnvM;UEkzTVU*qnXSa&pLTc~f(22p1}1ma;!!+Wf-6NI>5!yqpyg}KqCDC)Na5L^QSH-aVv_ag`);1N8CU<|<&g2xa{ zBRGTLVFceq@DB){MesZVyp=27Qnk~dxWvnscnOl+4u?8>-G0%66aHmXi8cls9Hg3OVl7`g>X%Fo4VL>I^H%- z7!xY>&;w0Xjnp1WRm1@axtGeS5E4@U04`}rFNO*@6bX(Ehyxd9)^V~)B}Vd_H^2AZ zZ{K?}p85PFIl=$nbhZOJM#>X~nskR3;MU{RFkI~}^~gOCC_r%(g;KBFtLq(9_sf1= zcNU!`Q5LDr-vDxedb)~INs=Yq3X4HGsQ8uc8&D1@qT;4=SP3W|I!Dx~;{7-c017ES zIi|+sxY8phlukLRbjc~zeIN8Gf*QXM9&mEn1k{k4R%2>HO{%F!y)_HV7XS#B;WErf zt#%jHIQrZEwZ3L?n$3!< zDv`%_(G9T$o3RN?H77OGQ4Nwiwv;QRd7Enhl+GF?|2hU&t6?0*&K26UY6N?Cv?vy^ zSI1&l#DcEHaR`gLme8^-8XQr&R!jiYU=yh(4Yue3TgqUI?bzs^=%bo87~@J8S#K9p z`>Fd3*>~gQeW18;gh~&Ya`ZU8IHe=eUBrjeIuhGKB&!?KJ!txe1l*@v6AXD^X%#>> z+g9zz{bb#7`OttNr-1{-WDVK$;;ha!ptGwTvu6hJAj%j#{MvY{WzBt%&N79cXW~Rs`}M9uH}6 zwSY?}$duiEB-jd7LXW~Vc8=(%4&&iVC!4xaDiQsBhwXsckCS6o(KTWyFN#Mh!%S?n@@E zM`rLS?XB4ueK4PcT&w3=1@6tXRAC=`pU!L8vsdfX-fzXf65aoQ(VeVadu_@LkUO1U z)L-ig!usv*X_y>9K}y`<7RpNvTSY?`v_eTm>@GnbCfLP*e8lR>LKivYc>CZqkMIngTggf|H}U-Y_oYceEpBemw_?( zbA2mt3x1fLm>vfk>EL@4;Hh<(>Q5)^(_`Ewmz*BtHZx|5Yz|r&GR84|d|)Qa{TL5W zxdpn;`M51#nCavEjGcRC@1XiKFF%v!o~11_S+X8H5r2nfhfc8+>5T@$VIP( z)q=W&FkL+%eIY-T>c>NC5MC!+;SqP!XV zOKL8sH>P2&pgG2Tpc=%={01P21?vs5y30XEFxc3XO&M`x(=29;Wc0yWxECmE3pu@v6;JJA z_F&@YJkdQYm@U~7413TtCVL=8jSog&DjSJujEM$)m=7+Q&x_~Wa>wm_@FxAA|M`FX z{+~G|r{0~g{Ae~C0R6r_d^D@~U9)iT`!Avcu;R>fDGmZ2@Wz}o?@GBO-jp-vyHZ`0 zH(v!QH`Q8lp1ddJk+d+^o$BUYyzMGXdHF8Bot{44&D-he7kc=Pi!lI@k9VX3LLe37 zJ5wRvnhNu6sfb{|0eGhnyaCqLsi+Eg1+Nel0zya#3z56rrk3Rh00bpif^pABs_WG| zs=pzeAs=gwY6kiTlVnU~C%Q0RZ2m{>k5!J zl)L%1vRCY-Cenui?!qo?!8)wRc5K7l7-B8fU={XmS{gQGA9>UowfaTJo)h40n~>bp zB`ZC+2bzI{yvz$V;_6IJMf&*)+b zT}alEU4vu18RLM$E+X6Q;rA*ku;VCge+Ldw>L7lj)7*(;l7tSVAt4oGOKS%X$#*k{ z4@eeaS<>Uey^N+IX)<@yI&8eew>W?+{qzDsv$hooNCFW&_)?1sayQy2a8b~p|>;a z7SV%}sbmTQaY7Cg0%)K-tZ)tEVX|d$J#`8vhTj}6nRrm9=PFzUm(|l?6;h1-ZoDF+ z0YHAWddP&;7L4IRI?wpJdcT{~G`iA@1g!CKcT@BOl2=a5yPKsYaFO?%Mno%nxmxexLFvun2Sbb@@?_K9(&g+l(haqEvy->F7y zUZ@(8p0;375XEd^K0TA2Px4Evh1^nxPcNfvK}@2RLVqcf&sE3UCt;))byI}8y0Elb z*A+!HCuZ{kVy_~yGt$9`gKXIo&_W*Cza+OiuBs2!9i*q-L0)ti17T(mVI)LJy-f;| zE{Yi>rX?&_HSg$!27NJeNl52~;zIR$XABw*OT}V(cBwFzohM&8-I_zxoBZZXl1I*# z80unJoeeCpv@E3Mf|h0$(Hm6BA{M`?9^tm2dI;Ik1OWNeyyvW zp01@9YZos+0$>e}sUE8#cS2Q7c0Gh0Tz}1(tR?4aj`_C@yE;d$vv-5|JVS7|I_Y^2 zzBc;Yq!!dhyyx`br_KS&Z|n7wD)n}I_qBafqw4!Z9!eir zZRf-42VpGdEbZdEi&mJv`fuuofpLP@+^-&pblH) zx_=IO$P<6G@-z@?9#vd>7O~JM%7~W{A0=^BLmY?a<1uLm>Mg~E6)~Gb1I(tKlDaN~ z=9e?5DD3SAQ#cvnnAT97McHLB&1X@)h1~ABQmNDI3IfXHkcnBD8DW2sjYVS{h}gR) zO;##hs-mt-rwf_9kWNdDs<#%=ECYR}5X36{3?L6fkb0pT%`fpQIpH|ENo5ddXdize XcLT2zYmjSsRRztDd6EduXqd~th27^B diff --git a/__pycache__/test_load_json.cpython-312.pyc b/__pycache__/test_load_json.cpython-312.pyc index a143cc80e6c6776b3b50de2163598c27f289e556..d14e8d7c91c6acf2d3117117dc7603c7eac9fa43 100644 GIT binary patch delta 1666 zcmaJ>U2GIp6uxJtvp=)Dv$H$ortaK~k$0BqkF|ZGi${m|7w8C@^ zwArB|R@S?zjj?hQD4ygXQ z-ZD~|@qX%N-T@k+UN;*Cl@RqEh!LXp7>Hudv|}n1G^VSRs!S(FR>(fTQcXQHFcX}v z(EorCAZ#wFyX{X$Xeb?3tdm5EP7+#qmP^;LC_$xWfHBmwEE$0jh({mm`WsJ^M52*t~*?qJx+qaJns5+BXhO_%h^kCmnyap+OSO_^q`bnO{0@teTH0%b;MMCnn zoGl~kRcc>zJhvv>qQ}=86%K47WZ3Wt96y`0F7q$gOZdI98UpycQIUh^An(tvD=7&X z^JStq8x8S+oG;UXADNncp>r?|rTJ{A?3Y=OhkY{sVhTg2j^to2FjF~gD9rqRLou|X5##`8heUtvTY2MtWhkqtm$?%AV!liieRcM+j^bgQvvnKIdH@qv z+B?9AZ`iwdm1hi|v`>N!o5Wp4Nq2DKhvIqY!Gxm&2OUjCa@e7rchnm~53PaI$vJD) zUCDDsoRe1nAbA$v2I7h>sMOc zsV?Ow-*)o7CR4$n^98}uThA3_M{kTPL_x0h_q1uxNCkYRtqSfn!c}b|yu`yJEx&r41DEz^{ACW}*jazt zT3oA=!TI_XhP(WyYoA&!up?^CnKC|oJ2Xc_E!c7bHIK3t$qUCX;oimusAU%^gqIuZ z@HQ{wPmM{u-RR~;%FkG^E8)S~gg;~{AF8~RbL1plPlZt{i{iw}L^WPa_@M!N%^tgT z`KSRwvDu4D2?wrYeQmSdIP_A^o@vmFaaK@!fQ2ca>gUegO&johlVp`f*(T=soJ`I5 zRZ|F@c)zLjKT-t1g|X&UEP*)2ABAB9d#jvz-a@jiEJ3m5?8|ww8_Fzu9-iMRc2`Wh zNDaxhmFHFxmA!e{j`BEXr}B)uybM70kmIY4kfYolas=Yu9h>8vdWxO8?v|KAb>mpe zYtqjp>DsP1sM8Fizx4{vwk8Z}82@U$W!%bg51+w1ZBJlcyR~ivQVSx-Z-V>g>M7ft zx&E0%c%dMK=PDxeR&o5*rDrm+1s4eveQKt*e?jkSv@gKu3mf=RbwMQa1C2voh zyBUL?xOS!EU0!TtiN?viD>&XWFNd`5jTZq{;*+jz!QxH1XX;mm6t(Z5YGg4~lWxqo zHF*Jk#NU$Dp2!0dc|_JcAXN`Ybg|+c0N3IWhPzvweE^Qd&71`sPrY>zSxBSOy~Q3W TxS~_-Q+1b;1!*l_?GFA82`q_M delta 1904 zcmZ`(eP~-%6u&1gFL^KDc`x5do1~pvQ`@Xbzs=CLbIx|{)>s4=*h<%%bA(hT>_gac5wP;J9@Suh z+MQUWXcrD(JAHE}J=k%)3?VMvu0lyJRY!VCMdAl;r!VPC#T25?F6>MeV;goYxaZaD z9zYcNQhr||fIW#|Qhy#{Z!)OJ3+hA(jqOgB^rK`D+Y0+eVH85ahf0|JA-K)94)pXT zsp1$;_YY>ekM{IthSHOaVrq2;h>jw`h(LEV?V~a*h)wG7R8XJKnZyTG)4dZ#x6Kv|zm=%|WTr+(F`$@04k*Nwt$j9cW z#zY+>8hOQBsS@rPtTQdsZ9f~VxdA{zS#DVN33~xr2^QagHgZjj961m}ivh=hFtb?4 zQT=+Xt~03mwxK-Eqopu=ph3G-t=N}hd}lF}Rhv8AOjZO%ce9EN@^KBDEw{#jT?Urw z%RC;9QaozNnYIGVAOQcLKX;Y!KuVN_7k#DNX$?k}a{2k+o zAyQ9HMqEx^zLmz!8U03VlnN`Y_PhzR3kAPLGD{XE0oIREn3=`53sQS4Z7=8I2Vg`0I1!W=}?Rj1Hf z%t3StLLOqPsj`n*N0OQDp>*chP+z)d_*j2$fOgI+H686^!hbxGEo{->*GYw&LXe)P z`WBdKa;fGr1V~qHEqSkYNwbH}rIG98NZlXSJDf3V3C=z@>&%)$D_r?ca!36rO#G(i zv%2CHwLfQI*==_?|Eyt!t5m?Ik`=D>k$_lTRu@=N2k+@klWmV5*^$Asic|`=bhiBp zzQyfcNB_u`=2&WfG9plNH?59+HRxtdu-(Oc=L9NyH)h}3DU|Wg(7#;Sc#O3~Da}cA zPw-6c%W=r&lP|sa89*u7*VJmit?f$=CeuA9(zJ!>)KP?%eBSga{6b!D_6woAsNyav zx{FFy)foU`^#pmhxyFhC%&YAT59Yrd6%foFCf_&j4TZ8o`CM@B)wvhGh-WSJldbpF Z!c61YBeP|5dv2+tIVVzk(uZh@e*k$u!PNi& diff --git a/compute_print_net.py b/compute_print_net.py index 712af42..f73bb49 100644 --- a/compute_print_net.py +++ b/compute_print_net.py @@ -487,14 +487,14 @@ def arrange_box_correctly(obj_transformed, voxel_size,total_matrix): pcd.points = o3d.utility.Vector3dVector(vertices) # 降采样与特征计算 - pcd_downsampled = down_sample(pcd, voxel_size) + # pcd_downsampled = down_sample(pcd, voxel_size) + # points = np.asarray(pcd_downsampled.points) + points = np.asarray(pcd.points) - points = np.asarray(pcd_downsampled.points) cov = np.cov(points.T) center = obj_transformed.get_center() - # 特征分解与方向约束(关键修改点) eigen_vals, eigen_vecs = np.linalg.eigh(cov) max_axis = eigen_vecs[:, np.argmax(eigen_vals)] @@ -730,7 +730,7 @@ class Platform: else: start_y = my + extend_dist_border_y_min final_y = self.depth - print("final_y2", model['name'], start_y, final_y, my, extend_dist_border_y_max, px) + # print("final_y2", model['name'], start_y, final_y, my, extend_dist_border_y_max, px) for y in range(start_y, final_y, +1): if self.can_place(px, y, z, model)==False: y -= 1 diff --git a/config.py b/config.py index 390eddc..997ab34 100644 --- a/config.py +++ b/config.py @@ -24,6 +24,7 @@ redis_config = { url_send_layout = 'https://mp.api.suwa3d.com/api/printTypeSettingOrder/printTypeSettingOrderSuccess' url_get_info_by_printIds = "https://mp.api.suwa3d.com/api/printOrder/getInfoByPrintIds?print_ids=" +url_get_oss_suffix_by_orderId = f"https://mp.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id=" # -------------------------- 结束:网络配置 ------------------------------- diff --git a/download_print.py b/download_print.py index c387513..aa28b94 100644 --- a/download_print.py +++ b/download_print.py @@ -16,6 +16,7 @@ import argparse from config import print_factory_type_dir from config import oss_config from config import print_data_dir +from config import url_get_oss_suffix_by_orderId from general import is_use_debug_oss from general import transform_save_o3d @@ -1291,19 +1292,13 @@ def read_pids_from_json(pid_file): return list_model_info, data def download_data_by_json(model_info, workdir, oss_client ): - ''' - 下载卡通化数据 - ''' + try: pid = model_info.pid model_height = model_info.model_height - # target_dir = f"{workdir}/{pid}_image" target_dir = f"{workdir}" - - # {"code":1000,"data":"base_cartoon/badge/101/3/init_obj","message":"success1"} - # https://mp.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id=879312 - url = f"https://mp.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id={model_info.order_id}" + url = f"{url_get_oss_suffix_by_orderId}{model_info.order_id}" res = requests.get(url) data = res.json()["data"] @@ -1334,7 +1329,6 @@ class BatchModelInfo: count: str def read_paths_from_batch(batch_id): - url = f"https://mp.api.suwa3d.com/api/printOrder/getInfoByPrintBatchId?batch_id={batch_id}" res = requests.get(url) diff --git a/download_print_out.py b/download_print_out.py index b9a29c6..03c0d1f 100644 --- a/download_print_out.py +++ b/download_print_out.py @@ -13,6 +13,8 @@ import requests import json import shutil +from config import url_get_oss_suffix_by_orderId + def download_transform_save_by_json(pid_file, workdir, oss_config): layout_data = download_datas_by_json(pid_file, workdir, oss_config) original_obj_pid_dir = workdir @@ -221,7 +223,7 @@ def download_data_by_json(model_info, workdir, oss_client ): model_height = model_info.model_height target_dir = f"{workdir}" - url = f"https://mp.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id={model_info.order_id}" + url = f"{url_get_oss_suffix_by_orderId}{model_info.order_id}" res = requests.get(url) data = res.json()["data"] diff --git a/general.py b/general.py index 027aed8..8be3749 100644 --- a/general.py +++ b/general.py @@ -258,7 +258,7 @@ def is_same_obj(obj_name1, obj_name2): pre_name1 = obj_name1.split("_x")[0] pre_name2 = obj_name2.split("_x")[0] - print(f"pre_name1={pre_name1}, pre_name2={pre_name2}") + # print(f"pre_name1={pre_name1}, pre_name2={pre_name2}") if (pre_name1==pre_name2): return True diff --git a/point_cloud_layout.py b/point_cloud_layout.py index 5194169..2b2934c 100644 --- a/point_cloud_layout.py +++ b/point_cloud_layout.py @@ -9,18 +9,12 @@ import open3d as o3d import numpy as np from plyfile import PlyData, PlyElement -from config import print_factory_type_dir - from general import mesh_tranform_to_pcd from general import need_upload_result from general import read_mesh -from compute_print_net import get_models_bbox from compute_print_net import arrange_models_on_platform -from compute_print_net import Platform -from compute_print_net import down_sample from compute_print_net import compute_bbox_all -from compute_print_net import compute_bbox_all_ext def make_bbox_for_print(base_original_obj_dir,dict_bad,dict_origin,is_downsample): """获取需要的盒子大小""" @@ -87,7 +81,6 @@ def get_pcd_by_matrix(dict_mesh_obj,dict_total_matrix,dict_ply_name): return dict_pcd_fix - def ply_print_layout_platform(dict_pcd_fix,dict_pcd_fix2,machine_size,dict_total_matrix,all_models): """根据排版结果移动点云到指定位置""" # placed_models,unplaced_models = get_models_box_size(dict_fix,machine_size) @@ -144,32 +137,6 @@ def ply_print_layout_platform(dict_pcd_fix,dict_pcd_fix2,machine_size,dict_total return placed_models -def get_models_bbox_net(dict_pcd_fix): - return get_models_bbox(dict_pcd_fix) - -""" -def get_models_box_size(dict_fix,machine_size): - #获取排版的盒子大小 - models = [] - for ply_file in dict_fix: - bbox_with_text = ply_file.split("=") - bbox_with = bbox_with_text[-1] - split_text = bbox_with.replace(".ply","").split("+") - extend_dist = 2 - x_length = int(float(split_text[2])*100) + extend_dist - y_length = int(float(split_text[0])*100) + extend_dist - z_length = int(float(split_text[1])*100) + extend_dist - #print("get_models_box_size",x_length,y_length,z_length) - models.append({'name':ply_file,'dimensions':(int(x_length/100),int(z_length/100),int(y_length/100))}) - print(models) - - platform = Platform(int(machine_size[0]), int(machine_size[1]), int(machine_size[2])) - print("开始计算排序...") - platform.arrange_models(models) - platform.print_results() - return platform.get_result() -""" - def compute_distance(pcd1, pcd2): """ 正确计算两个点云之间距离的函数。 @@ -187,44 +154,6 @@ def compute_distance(pcd1, pcd2): # return min_dist, mean_dist, distances return min_dist -def compute_distance_x(pcd1, pcd2): - points1 = np.asarray(pcd1.points)[:, 0] # 提取所有X坐标[3](@ref) - points2 = np.asarray(pcd2.points)[:, 0] - - x_diff = np.abs(points1[:, np.newaxis] - points2) - return np.min(x_diff) - -def compute_distance_y(pcd1, pcd2): - points1 = np.asarray(pcd1.points)[:, 1] # 提取所有Y坐标[3](@ref) - points2 = np.asarray(pcd2.points)[:, 1] - - y_diff = np.abs(points1.reshape(-1, 1) - points2) - return np.min(y_diff) - -def check_collision_x(pcd_moving, static_pcds,collision_threshold): - moving_points = np.asarray(pcd_moving.points) - min_distance_to_x_axis = np.min(np.abs(moving_points[:, 1])) # Y 坐标即为与 X 轴的距离 - #print(f"与 X 轴的最小距离: {min_distance_to_x_axis}") - #print(f"与 Y 轴的最小距离: {min_distance_to_y_axis}") - #print(f"pcd_moving{len(static_pcds)}") - if min_distance_to_x_axis < collision_threshold: - print(f"与 X 轴发生碰撞! 最小距离: {min_distance_to_x_axis}") - return True - - return check_collision_all(pcd_moving, static_pcds,collision_threshold) - -def check_collision_y(pcd_moving, static_pcds,collision_threshold): - moving_points = np.asarray(pcd_moving.points) - #print(f"与 X 轴的最小距离: {min_distance_to_x_axis}") - min_distance_to_y_axis = np.min(np.abs(moving_points[:, 0])) # X 坐标即为与 Y 轴的距离 - #print(f"与 Y 轴的最小距离: {min_distance_to_y_axis}") - #print(f"pcd_moving{len(static_pcds)}") - if min_distance_to_y_axis < collision_threshold: - print(f"与 Y 轴发生碰撞! 最小距离: {min_distance_to_y_axis}") - return True - - return check_collision_all(pcd_moving, static_pcds,collision_threshold) - import numpy as np def compute_aabb(pcd): @@ -349,9 +278,6 @@ def compact_mode_for_min_dis_json(placed_models,dict_unplaced,dict_bounds_fix,ma else: pcd_second.append(pcd) - # pcd_all.append(pcd_downsampled) - # name_list.append(model['name']) - # model_list.append(model) dict_name[pcd] = model['name'] dict_model[pcd] = model dx = model['dimensions'][0] @@ -374,7 +300,6 @@ def compact_mode_for_min_dis_json(placed_models,dict_unplaced,dict_bounds_fix,ma if max_delta_x < 10: draw_down = False - # for idx, pcd in enumerate(pcd_all): for idx, pcd in enumerate(pcd_first): if dict_model[pcd]['first_line']: @@ -1001,15 +926,6 @@ def compact_mode_for_min_dis_json(placed_models,dict_unplaced,dict_bounds_fix,ma #""" last_pcd_processed.append(pcd) - """ - print("is_x_top",is_x_top) - if not is_x_top: - last_pcd_processed.append(pcd) - print("last_pcd_processed.append",name) - else: - print("fail last_pcd_processed.append",name, is_x_top) - """ - # o3d.io.write_point_cloud(os.path.join(output_dir, name), pcd) dict_total_matrix[obj_name]= T_trans1 @ dict_total_matrix[obj_name] @@ -1029,19 +945,6 @@ def move_to_top_left(pcd, edge_x_min, edge_y_max): return T_transTemp -def move_to_bottom_left(pcd, edge_x_min, edge_y_min): - - points = np.asarray(pcd.points) - min_x = np.min(points[:, 0]) - min_y = np.min(points[:, 1]) - tx = edge_x_min - min_x - ty = edge_y_min - min_y - pcd.translate((tx, ty, 0), relative=True) - T_transTemp = np.eye(4) - T_transTemp[:3, 3] = [tx, ty, 0] - - return T_transTemp - def move_to_bottom_right(pcd, edge_x_max, edge_y_min): points = np.asarray(pcd.points) @@ -1066,229 +969,8 @@ def get_axis_aligned_bbox(pcd): 'z_max': np.max(points[:,2]) } -def down_obj_data_to_ply(weight_fix_out_obj_dir,weight_fix_out_ply_dir): - """""" - obj_file_list = [aa for aa in os.listdir(weight_fix_out_obj_dir) if aa.endswith(".obj")] - for obj_name in obj_file_list: - obj_path = os.path.join(weight_fix_out_obj_dir,obj_name) - - mesh_obj = read_mesh(obj_path) - - vertices = np.asarray(mesh_obj.vertices) - pcd = o3d.geometry.PointCloud() - pcd.points = o3d.utility.Vector3dVector(vertices) - voxel_size = 3 # 设置体素的大小,决定下采样的密度 - pcd_downsampled = down_sample(pcd, voxel_size) - ply_out_path = os.path.join(weight_fix_out_ply_dir,obj_name.replace(".obj",".ply")) - o3d.io.write_point_cloud(ply_out_path, pcd_downsampled) - print(ply_out_path,"下采样完成。") - -def compute_centroid_compact(pcd): - points = np.asarray(pcd.points) - centroid = np.mean(points, axis=0) - return centroid - -def compute_base_point(pcd): - points = np.asarray(pcd.points) - x_center = np.mean(points[:, 0]) - y_center = np.mean(points[:, 1]) - min_z = np.min(points[:, 2]) - return np.array([x_center, y_center, min_z]) - -import copy -def move_obj_to_compact_bounds(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,base_original_obj_dir,compact_obj_out_dir,dict_mesh_obj,dict_unplaced,placed_remove_dir,dict_bad,bad_dir,full_dir,dict_bounds_fix,dict_compact,dict_origin): - """""" - # obj_file_list = [aa for aa in os.listdir(weight_fix_out_obj_dir) if aa.endswith(".obj")] - obj_file_list = list(dict_mesh_obj.keys()) - ply_path_dict = {} - - # meshes = [] - # for ply_file_name in os.listdir(bounds_fix_out_dir): - for ply_file_name in dict_bounds_fix: - ply_dict_key = ply_file_name.split("=")[0] - ply_path_dict[ply_dict_key] = ply_file_name - for obj_name in obj_file_list: - obj_path = os.path.join(weight_fix_out_obj_dir, obj_name) - - # mesh_obj = read_mesh(obj_path, False) - mesh_obj = dict_mesh_obj[obj_name] - # mesh_obj = dict_origin[obj_origin_path] - - original_obj_pid_dir = base_original_obj_dir - obj_origin_path = os.path.join(original_obj_pid_dir, obj_name) - obj_origin = dict_origin[obj_origin_path] - # obj_origin = copy.deepcopy(dict_origin[obj_origin_path]) - - ply_name_pid = obj_name.replace(".obj","") - # ply_name = ply_path_dict[ply_name_pid] - ply_name = ply_path_dict.get(ply_name_pid,None) - print(ply_name_pid,ply_name) - if ply_name is None: - continue - print("move_obj_to_compact_bounds",ply_name,len(dict_unplaced)) - if not ply_name or ply_name in dict_unplaced: - print("unplaced",ply_name) - continue - ply_fix_path = os.path.join(bounds_fix_out_dir,ply_name) - ply_compact_path = os.path.join(bounds_compact_out_dir, ply_name) - # pcd_fix = o3d.io.read_point_cloud(ply_fix_path) - pcd_fix = dict_bounds_fix[ply_name] - - vertices = np.asarray(obj_origin.vertices) - pcd_origin = o3d.geometry.PointCloud() - pcd_origin.points = o3d.utility.Vector3dVector(vertices) - - # pcd_compact = o3d.io.read_point_cloud(ply_compact_path) - pcd_compact = dict_compact[ply_name] - - centroid_fix = compute_centroid_compact(pcd_fix) - centroid_compact = compute_centroid_compact(pcd_compact) - centroid_origin = compute_centroid_compact(pcd_origin) - displacement = centroid_compact - centroid_fix - # displacement = centroid_compact - centroid_origin - vertices = np.asarray(mesh_obj.vertices) - vertices_translated = vertices + displacement # 将位移应用到每个顶点 - mesh_obj.vertices = o3d.utility.Vector3dVector(vertices_translated) # 更新网格顶点 - - obj_pid = obj_name.split("_P")[0] - #compact_obj_pid_out_dir = os.path.join(compact_obj_out_dir,obj_pid) - compact_obj_pid_out_dir= compact_obj_out_dir - if not os.path.exists(compact_obj_pid_out_dir): - os.makedirs(compact_obj_pid_out_dir) - obj_path_compact = os.path.join(compact_obj_pid_out_dir,obj_name) - mesh_obj.compute_vertex_normals() - o3d.io.write_triangle_mesh(obj_path_compact, mesh_obj,write_triangle_uvs=True) - print(obj_path_compact, "移动后obj保存完成", displacement) - - # meshes.append(mesh_obj) - - #original_obj_pid_dir = os.path.join(base_original_obj_dir,obj_pid) - original_obj_pid_dir = base_original_obj_dir - for mtl in os.listdir(compact_obj_pid_out_dir): - if mtl.endswith(".mtl"): - if obj_pid in mtl: - mtl_path = os.path.join(compact_obj_pid_out_dir,mtl) - os.remove(mtl_path) - - mtl_name = None - tex_name = None - for file_name in os.listdir(original_obj_pid_dir): - if file_name.endswith(".mtl"): - if obj_pid in file_name: - mtl_name = file_name - if file_name.endswith(".jpg"): - if obj_pid in file_name: - tex_name = file_name - if file_name.endswith(".png"): - if obj_pid in file_name: - tex_name = file_name - - for file in os.listdir(original_obj_pid_dir): - #print(f"file{file}") - if file.endswith(".obj"): - continue - if obj_pid not in file: - continue - - origin_path = os.path.join(original_obj_pid_dir,file) - dis_path = os.path.join(compact_obj_pid_out_dir,file) - - if os.path.isfile(origin_path): - #print(f'origin_path{origin_path}') - #print(f'dis_path{dis_path}') - shutil.copy(origin_path,dis_path) - time.sleep(1) - #print("-"*50) - base_origin_obj_path = os.path.join(original_obj_pid_dir,obj_name) - #print(f"base_origin_obj_path{base_origin_obj_path}") - #print(f"obj_path_compact{obj_path_compact}") - update_obj_file(base_origin_obj_path, obj_path_compact) - - placed_remove_obj_path = os.path.join(placed_remove_dir, obj_name) - shutil.copy(base_origin_obj_path,placed_remove_obj_path) - os.remove(base_origin_obj_path) - - exist_obj_any = False - exist_obj = False - delete_mtl = False - for file_name in os.listdir(original_obj_pid_dir): - if file_name.endswith(".obj"): - if obj_pid in file_name: - exist_obj = True - exist_obj_any = True - - if not exist_obj_any: - delete_mtl = True - if not exist_obj: - delete_mtl = True - - if delete_mtl: - print("delete_mtl",mtl_name,tex_name) - if mtl_name!=None: - base_origin_mtl_path = os.path.join(original_obj_pid_dir,mtl_name) - placed_remove_mtl_path = os.path.join(placed_remove_dir, mtl_name) - shutil.copy(base_origin_mtl_path,placed_remove_mtl_path) - os.remove(base_origin_mtl_path) - - if tex_name!=None: - base_origin_tex_path = os.path.join(original_obj_pid_dir,tex_name) - placed_remove_tex_path = os.path.join(placed_remove_dir, tex_name) - shutil.copy(base_origin_tex_path,placed_remove_tex_path) - os.remove(base_origin_tex_path) - - print(f"排版错误模型数量::{len(dict_bad)}") - for obj_name in dict_bad: - print("--错误模型名:", obj_name) - process_obj_files(original_obj_pid_dir,bad_dir,obj_name) - - print(f"排版剩余模型数量::{len(dict_unplaced)}") - for ply_file_name in dict_unplaced: - obj_name = ply_file_name.split("=")[0]+".obj" - print("--剩余模型名:", obj_name) - process_obj_files(original_obj_pid_dir,full_dir,obj_name) - import json -def extract_angles(R): - # 提取绕X、Y、Z轴的旋转角度(弧度) - rx_rad = np.arctan2(R[2, 1], R[2, 2]) # X轴旋转 - ry_rad = np.arcsin(-R[2, 0]) # Y轴旋转 - rz_rad = np.arctan2(R[1, 0], R[0, 0]) # Z轴旋转 - - # 将弧度转换为角度(度数) - rx_deg = np.degrees(rx_rad) - ry_deg = np.degrees(ry_rad) - rz_deg = np.degrees(rz_rad) - - return rx_deg, ry_deg, rz_deg - -def compute_mesh_center(vertices): - """ - 计算网格质心 - - 参数: - vertices: 顶点坐标数组,形状为(N, 3)的NumPy数组或列表 - - 返回: - centroid: 质心坐标的NumPy数组 [x, y, z] - """ - if len(vertices) == 0: - raise ValueError("顶点数组不能为空") - - n = len(vertices) # 顶点数量 - # 初始化坐标累加器 - sum_x, sum_y, sum_z = 0.0, 0.0, 0.0 - - # 遍历所有顶点累加坐标值 - for vertex in vertices: - sum_x += vertex[0] - sum_y += vertex[1] - sum_z += vertex[2] - - # 计算各坐标轴的平均值 - centroid = np.array([sum_x / n, sum_y / n, sum_z / n]) - return centroid - import re def extract_numbers_from_filename(filename): """ @@ -1313,7 +995,7 @@ def extract_numbers_from_filename(filename): import requests def move_obj_to_compact_bounds_json(base_original_obj_dir,dict_mesh_obj,dict_unplaced,dict_bad,bad_dir,full_dir,dict_bounds_fix, - dict_total_matrix,save_mesh,batch_id, print_start_time,selected_machine,selected_mode,version): + dict_total_matrix,batch_id, print_start_time,selected_machine,selected_mode,version): """生成3D打印布局的JSON数据并保存为3DPrintLayout.json""" # 创建符合3DPrintLayout规范的JSON数据结构 layout_data = { @@ -1429,10 +1111,6 @@ def move_obj_to_compact_bounds_json(base_original_obj_dir,dict_mesh_obj,dict_unp print("--剩余模型名:", obj_name) process_obj_files(original_obj_pid_dir,full_dir,obj_name) - if save_mesh: - cache_type_setting_dir=f"{base_original_obj_dir}/arrange" - transform_save_o3d(layout_data, original_obj_pid_dir, cache_type_setting_dir) - return send_layout_data def process_obj_files(original_obj_pid_dir,placed_remove_dir,obj_name): @@ -1494,31 +1172,6 @@ def process_obj_files(original_obj_pid_dir,placed_remove_dir,obj_name): shutil.copy(base_origin_tex_path, placed_remove_tex_path) os.remove(base_origin_tex_path) -def update_obj_file(original_obj_path,compact_obj_path): - """""" - with open(original_obj_path, "r") as f: - lines_original = f.readlines() - mtllib_name = None - mat_name = None - for line in lines_original: - if line.startswith("mtllib"): - mtllib_name = line.split(" ")[1] - elif line.startswith("usemtl"): - mat_name = line.split(" ")[1] - if mtllib_name and mat_name: - break - with open(compact_obj_path, "r") as f: - lines = f.readlines() - new_lines = [] - for line2 in lines: - if line2.startswith("mtllib"): - line2 = f"mtllib {mtllib_name}\n" # 替换为原始 MTL 文件路径 - elif line2.startswith("usemtl"): - line2 = f"usemtl {mat_name}\n" # 替换为原始贴图路径 - new_lines.append(line2) - with open(compact_obj_path, "w") as f: - f.writelines(new_lines) - def pass_for_min_dis(placed_models, dict_unplaced, dict_bounds_fix): pcd_all = [] @@ -1546,23 +1199,3 @@ def pass_for_min_dis(placed_models, dict_unplaced, dict_bounds_fix): name = name_list[idx] print("fail to place (x=0)", name_list[idx], y, dy) dict_unplaced[name]=name - -if __name__ == '__main__': - out_dir = "/data/datasets_20t/type_setting_test_data/" - weight_fix_out_obj_dir = f"{out_dir}/print_weight_fix_data_obj" - weight_fix_out_ply_dir = f"{out_dir}/data/datasets_20t/type_ssetting_test_data/print_weight_fix_data_ply" - - base_original_obj_dir=f"{print_factory_type_dir}/8/" - if not os.path.exists(weight_fix_out_ply_dir): - os.makedirs(weight_fix_out_ply_dir) - bounds_fix_out_dir = f"{out_dir}/print_bounds_fix_data" - bounds_compact_out_dir = f"{out_dir}/print_bounds_compact_data" - compact_obj_out_dir = f"{out_dir}//print_compact_obj" - if not os.path.exists(bounds_fix_out_dir): - os.mkdir(bounds_fix_out_dir) - if not os.path.exists(bounds_compact_out_dir): - os.makedirs(bounds_compact_out_dir) - if not os.path.exists(compact_obj_out_dir): - os.makedirs(compact_obj_out_dir) - - move_obj_to_compact_bounds(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,base_original_obj_dir,compact_obj_out_dir) \ No newline at end of file diff --git a/print_factory_type_setting_obj_run.py b/print_factory_type_setting_obj_run.py index 7333707..77b244e 100644 --- a/print_factory_type_setting_obj_run.py +++ b/print_factory_type_setting_obj_run.py @@ -13,6 +13,7 @@ from test_load_json import load_show_save from download_print import upload_result from config import print_factory_type_dir +from config import print_data_dir from config import url_send_layout from config import big_machine_size from config import small_machine_size @@ -101,12 +102,11 @@ def print_type_setting_obj(base_original_obj_dir=None,batch_id=0,selected_mode=" os.makedirs(full_dir) is_small_machine = True if selected_machine=="小机型" else False - save_mesh = True if output_format=="模型" else False version = "print_type_setting25.local" send_layout_data = move_obj_to_compact_bounds_json(base_original_obj_dir,dict_mesh_obj,dict_unplaced,dict_bad,bad_dir,full_dir,dict_pcd_fix, - dict_total_matrix,save_mesh,batch_id,print_start_time,selected_machine,selected_mode,version) + dict_total_matrix,batch_id,print_start_time,selected_machine,selected_mode,version) end_time4 = time.time() print("排版完成") @@ -151,7 +151,6 @@ def print_type_setting_obj(base_original_obj_dir=None,batch_id=0,selected_mode=" print(f"need_upload_result={need_upload_result()}") if need_upload_result(): print(f"send_layout_data={send_layout_data}") - # url = 'https://mp.api.suwa3d.com/api/printTypeSettingOrder/printTypeSettingOrderSuccess' url = url_send_layout try: response = requests.post(url, json.dumps(send_layout_data), timeout=30) @@ -180,35 +179,19 @@ def print_type_setting_obj(base_original_obj_dir=None,batch_id=0,selected_mode=" return 0 -def preview(base_original_obj_dir=None, batch_id=0): - - load_show_save(base_original_obj_dir, {}, batch_id, True) - -def get_pcd(obj, is_down_sample=True): - vertices = np.asarray(obj.vertices) - pcd = o3d.geometry.PointCloud() - pcd.points = o3d.utility.Vector3dVector(vertices) - - if is_down_sample: - voxel_size = 3 - pcd_downsampled = down_sample(pcd, voxel_size, False) - return pcd_downsampled - else: - return pcd - if __name__ == '__main__': # parser = argparse.ArgumentParser() # parser.add_argument("--batch_id", type=str, required=True, help="batch_id") # args = parser.parse_args() # batch_id = args.batch_id - batch_id = "9910032" + batch_id = "1" src_dir = batch_id selected_mode="紧凑" # 标准 紧凑 output_format="JSON" # 模型 JSON selected_machine = "大机型" # 小机型 大机型 - base_original_obj_dir = f"{print_factory_type_dir}/{src_dir}" + base_original_obj_dir = f"{print_data_dir}/{src_dir}" print_type_setting_obj(base_original_obj_dir=base_original_obj_dir, batch_id=batch_id,selected_mode=selected_mode,output_format=output_format,selected_machine=selected_machine) diff --git a/test_load_json.py b/test_load_json.py index 1224fc5..b7768dc 100644 --- a/test_load_json.py +++ b/test_load_json.py @@ -357,7 +357,7 @@ def set_orthographic(meshes, width=1920, height=1080, del mesh del points gc.collect() - print(f"set_orthographic {index}") + # print(f"set_orthographic {index}") index = index + 1 del meshes @@ -406,7 +406,6 @@ def set_orthographic(meshes, width=1920, height=1080, view_control.set_up([0, 1, 0]) ortho_width = ortho_width or 10.0 - print("set_orthographic 1") #for _ in range(2): # vis.poll_events() # vis.update_renderer() @@ -414,10 +413,8 @@ def set_orthographic(meshes, width=1920, height=1080, # 设置正交投影 try: set_orthographic_camera(view_control) - print("set_orthographic 2") except AttributeError: set_orthographic_projection(view_control, ortho_scale=15.0) - print("set_orthographic 3") #for _ in range(5): # vis.poll_events()