Marker Gerierer

This commit is contained in:
chk
2026-05-30 15:47:58 +02:00
parent b3cf7adc95
commit 50baeae449
9 changed files with 2538 additions and 0 deletions

View File

@@ -0,0 +1,303 @@
{
"page_format": "A0",
"orientation": "portrait",
"page_size_mm": {
"width": 841.0,
"height": 1189.0
},
"seed": 103,
"num_arucos": 20,
"aruco_size_mm": 25.0,
"aruco_dictionary": "DICT_4X4_250",
"aruco_start_id": 103,
"page_border_margin_mm": 50.0,
"forbidden_rectangle_mm": {
"x": 318.5,
"y": 94.5,
"w": 204.0,
"h": 1000.0
},
"forbidden_rectangle_margin_mm": 30.0,
"placements": [
{
"id": 103,
"position": [
-453.5,
-331.0,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 104,
"position": [
-749.92,
-144.69,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 105,
"position": [
-51.03,
216.13,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 106,
"position": [
-94.29,
-254.46,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 107,
"position": [
-204.73,
311.12,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 108,
"position": [
5.69,
-222.91,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 109,
"position": [
-82.1,
208.77,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 110,
"position": [
-524.92,
278.61,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 111,
"position": [
-403.65,
238.66,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 112,
"position": [
-360.75,
191.8,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 113,
"position": [
-267.34,
190.31,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 114,
"position": [
-1017.49,
215.5,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 115,
"position": [
-919.81,
-264.8,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 116,
"position": [
-480.39,
157.71,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 117,
"position": [
-415.03,
265.79,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 118,
"position": [
-70.87,
-288.32,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 119,
"position": [
-712.35,
-212.06,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 120,
"position": [
-484.2,
305.52,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 121,
"position": [
-1018.02,
-343.04,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 122,
"position": [
-278.45,
-137.41,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
}
]
}

View File

@@ -0,0 +1,68 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 2383.937 3370.394 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (OpenAI) /CreationDate (D:20260530154201+02'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20260530154201+02'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (A0 poster with random ArUco placement) /Title (A0_20Arucos_25mm_Seet103) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3859
>>
stream
Gasba]ok34&OlYDMRG6m;ZCco!$!QbhLK!RfJV7=oh!\u1'OrHhUmXDhOZNlRn$I)WERh&T;_e2oC'Hor;$9La632HP)lEbO.SU66M^QSbNFpN^@X#UkLY34rUQU1NuVd"T:C6:H'Zd[s7lNa]<D#-oWNcD8n$f/9)JNjIq-Les88\D_s$2\q[!&0J!.Md(8ekG[kIR%hj0LAR%o&7BKJ)"IeL8!CbbS05QB/=s5c2r?`\YTM)9PdP1l2G4lhk-RuO&e^N9?sQoI]/qJs@D[f=*\065W?VgNNT&nD9#MW?JVGa#dl^0a"YOX,o?A:C<b$+8BGM#Ollj=e5/B<`b;Q8u=L:l94.qp&!B]nOO>(uN@A`L;OL6QV5&QXG$L,M"!D;]Zd!HXPKsQXMJLoFU'@1(^Nu+BZgk?f4;75qN/S>(jA5+os%b3C0<]-t(PN!Ma6,U!fYJZ[HEt]qjnXjMm'`G%0Xe<'lneZ+9+!FE23^6,sQEXY]T!*4n)X*42ISeLnc-G.!rTPrnl/`h5Bp_sReVr:H<Cm-Z`cIH**HNm"XgmML7%cZaOSO:;$+A^DHa@GeR-ogRFDT86.FDbj3rHi?k.[g!1f2%XTkHGQ"*l&7%T[4gEli&\6uo`i7@Vuq@/9X-V\b29N\bj+go9'06`4):PB_h66:;n5U?%M+Xa4CH`'@+IJKeihsFgdBIdX59]tLBkmA*\cn$0^Bc6lQ1"^m\/pHG+/s^*[#fS^oZ.B=g&:tp"J+.Xl&8D7r=>Nq@0d&(ME6M\kFM_>_F.]!DtKYU>BMoFTi&9\_a@ZPPUeX>US\i;\#oeNm0GG[8(@:*IuQT*31;\S<IH0$p)G(d&fUk&+-qnE"*5f2)+CJ^p%#F_nI$+e21iujgn''$Q(`-(c;O82$FiQ"u'`m1ik.u"bI[9=K@?fXY>@_PnBs[VHIBA:1:gmK!jG]fH$;D/T0,3pjL-HE=(4U'!1aE_-4#Sm+)0a*/C]JRgqMkkX+MldEF%o;EBNo'N,s>5t_Nh9a\![N"IMGF^60tKt;i7CThnC`X`6?FU+:V$(,iCQurk*2%N]/mF?,&O4Qk*QBtu20>Vs@>pIJ$*p&t0Zdooqla:0PCW!7*Q?/Gb[LPK9_lVIE=g&:@?5mLi[\A6pEgIbtFUsj^&"%JMQurq$Q*HYK.4l]',e:$oAUPD\FDajYCPc.#a0$_T%Y>D,Q9.,5;X@R'kTEF$k$cgPdqrrKGT^,[5d9C%@*JeiRKnH#*[J0JnoZs$[5%$nbHO&]_cgBo1]"fmoXk==qgr7Y2M/?j#hEb6H3N;'9`h`0(q$Rb[B)g#JGhN!pTOd9I7CHlB*;uc#hCgt[d":a\^CG)Ek_gGs3ETo>s*>a)-6FmZ#4^rX:dQkkLcVTICR;$9<WGp4Zqq8=MI7L^oVmuXF%aaHbc_0JF)5\Iqr@T9/o%AeidElCW!7*#2,b04dl/ZIn!kS@Fk$Zf(26%aK$\1Mq<=ThC`?"G%V:Rme>kg"(o6-1$ck@B&,+Ph#h1\N7nA,<UDS5\fEk$P$/T\9h#)&in3$Q8ss24#]X^n@b<`Z.-i)gJoqS[L3`aQm-oO.pOHE@J6DVs=ndGLo?^qRrAoFI%g6Yh#hD*LXEbpjo0BPJ5t\*-cclq1Qurq,FUsl4g+_j#V5A<cgO/_9J6D=Kb^*U>HJUc9gm/^G=$Y8^!hq:JU@&#UXoP.qqt.MNor7p-f3)@YDmKS9HYBg,Cmhl)kT&>FkM1]"HOb&>6*]idmBN56=e'iSXF%aaHbc_0^oVmuXF&<>6&jJ6T6nATHbc_0^oVmuXF%aaH`/0"\]uJ62U$JaR.\+Zot3FOHa0Q!_lRLdXCom_o%NP-A\bDE`N8sUX<0NkFUsR>1[<AVHr;LV&rc-O'CAq^Rj_<aTWH<R_i>GZZcdu/Vlgfd)[+o*%J/996+#r!C0PC3&"%JMQurq,FUsQs4Q(D=6dt#hG/NMl%AI/H7(k:-*d,;"]e!Flkq@&4Mck6(fcr+efCM`Dp\h0`?H=0*E4_E%@Li(B0>fRWX>qM-4)bJVNZ?9S#GsET^7;&?Vd>%[c<:Qr4[Quh]nA#TYsTl>.<4_(!BB8aY[)@D/;7OD%r&_g3cbh!FUsj^&"%JMO?G_aG25O:WVZ;*g7pRMH="Z6H#^Hf=,4V@^No&AL0:\hZ:A6VY34?oWnq[bH!\^@?Hi7c#2,b0gof8+6WQt6.>cZ2=g&:@?5mMTR"Xhb!OD#lA/fZ>p]`WYe[]kEY+7K_D0dYWYu@T#*@h>JVPZ`"UsM4jJ"A:0AShH/BXN<hXC;QfW!gD>$,EJG^o.RTA\A.em\[=AqZA-_<<Tn6,1%>FUD?ja:.DCU^cVV5DQaIaBeaCCZ_!3iI-1;I>6O83$.e)K)0Xf0CXqU(,iW:aNuEVX?6`S$5d7*dZa(WtPZFCqU)c?<33*Mu]l@j=cSP&)gN@2Vd>;>Q#UN6#:$ZN#Pdk>7L+]0Ok712g_eonl]/;;/eppoP^biY\$pBQco8H]gAh=)5m#EdmlkjE>s4\1q]Ak[5mKnX(4b6bb^bkq'W&d\tof4n'JRVsq'`2VffNfEGln2!;PnDF;q6r28h5)jm/ra;s*[Y"<JC.]/N[dtWZL/(\HG,)4Q$@aEYcS.5pD=aUO"f1,AdgbrN[gH+a84ALkOo,-Y'U^SIbc8lGegD-^oVmuXF#Kai;pFR*g3D2DN[%q_lVGofcr.ffCP=$FZ6d4pX(U(O*:3OYLa4ULBkmA)0!3Z6*Da!IZM=+8^)Keb^.$4erNDD*ZcG.ZKBEtHO,B*Ka'I@c"Fd)_6,F^Wb\S/Y[-k/bd!r,r1\/sCcVi'N\q@5S#>Zbp:CQ]2%Kh**XL=Z&?L."=SEs%_>^2fb^'K;HA7(oW^$=]ZgJX9Sen`)2kpW>%qh:&]U8BP!OH)OB"bX)*i_I$!jh:qZgIk#mK'Z^!O=cR;cS*+C"h:p<dJ=GRk(%>Z\23]a6K=_g>l?VZm+2),3*quQ!d==%USZdZlBtiehdr>BcpNYNF8ls1%f22Y>"QA#hE`8=K@EhG8CpkBcu'1@7JXAoD5S+<Z8mDdMV[0NBqc&$u;]]qEBH,$,:hS@8-j?ma;Bj8g@Fg/_,rmb7fG1@2QE?`TU?Q8Mblnku41OQR!>(!M8M5q0@:C@\l7q"[K>l5@4RZZ$EFS!hdo?K4X%Ef2/lA_1R?:JRD1S_3tR[[G!>#[<4V`_1R?*QXDBu_<u1c:b*!]?J9]Xp\n9gobp:o!_.B>R'a.Al;asaCr$d=ed2Ip&MRu<SgS0Sqk%.G_eupl]5(p/[j)`3CVXa]-.M**CH#2*hQP,5L&B`D+BY3+bB"2H@.ar#cTM\gePi>0pFlkRO2K[dTigp5@fm6X\d*gE%g6Yh#hE`8=KbF'GR3o>%C9@[B"^+H=s"EZHbc_08/"+iH$I-+B>CT]-itI,4T&[>rF4@V@b,>4XOgK/GS'N:Mcoe)=ndGLpOHE@!8/W5,Lpig;Y8HAm+P!1-c`BL<1QD.5,r8-&SBMG8%!5[1Mf[p>g?T/X^U@r4pI];0^Pik6+7&M[F1FchpQUhUF5t^k&$qlm!!.XqNf.*]8%j/eNd]u)PlW*iVM>bCEQa&^pL^0UnQs_iA*+RgD'j+ELCE_g_)u:lht=SbHPni#2McZ_:k$hYZh3AV2cDblB,!5TjJU[CCeuugSus=[U?MNc_!IFQ<r/:8J.mrdW6rRRV*22<*<G@R*Yf&K/.s[=$X"PJ7,K7,4A`>Nk2kdM+_OcCS`cPmPBURV(m7>BsPH!lah;Y9>di$+PPW`de\Qql^+r)"O,T9^p%F;@')70mk-2qn$9O]eaq3ClN[p9RKe@$%0UGb!;lTg&).b,Oo~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000817 00000 n
0000000876 00000 n
trailer
<<
/ID
[<69f276e09b5581926ba94255cb8d0210><69f276e09b5581926ba94255cb8d0210>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
4826
%%EOF

View File

@@ -0,0 +1,143 @@
{
"page_format": "A0",
"orientation": "portrait",
"page_size_mm": {
"width": 841.0,
"height": 1189.0
},
"seed": 123,
"num_arucos": 20,
"aruco_size_mm": 25.0,
"aruco_dictionary": "DICT_4X4_250",
"aruco_start_id": 103,
"page_border_margin_mm": 50.0,
"forbidden_rectangle_mm": {
"x": 318.5,
"y": 94.5,
"w": 204.0,
"h": 1000.0
},
"forbidden_rectangle_margin_mm": 30.0,
"placements": [
{
"id": 103,
"x_mm": 87.49,
"y_mm": 142.77,
"size_mm": 25.0
},
{
"id": 104,
"x_mm": 695.26,
"y_mm": 90.6,
"size_mm": 25.0
},
{
"id": 105,
"x_mm": 660.09,
"y_mm": 219.88,
"size_mm": 25.0
},
{
"id": 106,
"x_mm": 225.54,
"y_mm": 51.78,
"size_mm": 25.0
},
{
"id": 107,
"x_mm": 698.27,
"y_mm": 148.7,
"size_mm": 25.0
},
{
"id": 108,
"x_mm": 151.8,
"y_mm": 890.64,
"size_mm": 25.0
},
{
"id": 109,
"x_mm": 65.2,
"y_mm": 1019.46,
"size_mm": 25.0
},
{
"id": 110,
"x_mm": 649.89,
"y_mm": 868.0,
"size_mm": 25.0
},
{
"id": 111,
"x_mm": 198.11,
"y_mm": 697.87,
"size_mm": 25.0
},
{
"id": 112,
"x_mm": 600.8,
"y_mm": 595.57,
"size_mm": 25.0
},
{
"id": 113,
"x_mm": 589.99,
"y_mm": 243.08,
"size_mm": 25.0
},
{
"id": 114,
"x_mm": 637.26,
"y_mm": 1022.12,
"size_mm": 25.0
},
{
"id": 115,
"x_mm": 102.48,
"y_mm": 536.0,
"size_mm": 25.0
},
{
"id": 116,
"x_mm": 185.37,
"y_mm": 143.8,
"size_mm": 25.0
},
{
"id": 117,
"x_mm": 700.95,
"y_mm": 729.68,
"size_mm": 25.0
},
{
"id": 118,
"x_mm": 760.12,
"y_mm": 1075.76,
"size_mm": 25.0
},
{
"id": 119,
"x_mm": 760.24,
"y_mm": 522.95,
"size_mm": 25.0
},
{
"id": 120,
"x_mm": 588.18,
"y_mm": 96.95,
"size_mm": 25.0
},
{
"id": 121,
"x_mm": 146.46,
"y_mm": 255.54,
"size_mm": 25.0
},
{
"id": 122,
"x_mm": 233.56,
"y_mm": 424.58,
"size_mm": 25.0
}
]
}

View File

@@ -0,0 +1,68 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 2383.937 3370.394 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (OpenAI) /CreationDate (D:20260530153005+02'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20260530153005+02'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (A0 poster with random ArUco placement) /Title (A0_20Arucos_25mm_Seet123) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3715
>>
stream
Gasb`]9;Qi&Ol<e$H<E\]H=,RCaY,,qZe`Z78fdH^TRk1l#%ochK>kuUR=!,h$Y>*nrQO@P^hrrn,IlaSpu39rpfILV#5W;rhLItoBk`(?f/TY>Oq6D?b_&>d9L8iX,Ns@&OT^oq;[h)qbLShrTSh3o9I3E[FbEU.DN.IO8jWK+(:3+Zn[2\4e=4^%NCU=s53"Kh`m:i$o+%n`2[8.p7rC78hp2Z5Q7I_D97!:VV%"tc])G2``bFDXql$u'O0Q&Z->VCCE?OCTpJN^MqOl]1deBtnj'GA!OG:&goffl1#t7'%$3V-i-%7NboudLNCh>8)0!3ZTp7=8EBO@kmARh!%gIi959TSpJC(Z6`gNgLmte(XDmn)_=UH)f.gKQ9P@8Ns""`'CYqig6'fX/a>rE<3f/0iQ>3;8Bkg=/6Tu]3'$6rA?!8/TKX:fmX;LJaPO%Z,]Q\]$hk6`tj\`!fsO%Z,]Q\_;S;bUWl9#Y@Dm0V#5dDN11%1K6TLq(<UPgeKOln#WB1/60#9'07'0P[<6]h5b73E@u$hDeP:Z8=V/f/uasghodSiIPC]"(p?k_3tU\2%N)]o<;a:p$2\?*XL=Z@+IIPXEbpjo0DA:%mtfsa)qP@%h,K[#hEa#=KRS)@'sS!_iU]\e9[*>/M%B>=\B,K?5H9G9<ctXR.\T-f(t>59`npQ=eXn>rAWC@h+j2\'4Lck'U)gEe]En?A@7[>FGd+2.<LCrq%eTdq->mrrG'L?a/B<[W#NOR/&85J^oU+hjfFEqB$;Q/e0JGO-",:M@+HW3eg/=?QKE%Bk7TK6Q!r5o_lQpo=fY4s-eUpdQ!r1R(S`#eZPkaj+%9p2,#A_AT[mTUCHY.k-3:Gn%NuT5Dk:uq'[qId\jSni$nLKEiaHe>&e?d<C_dO3F)78<`kn!Q)W\8@k5/K71dKVL&?':c=OTO>VL^-i2(UcTlK*=\85!GhNQ\OE(UHWe,b)%(0$N>gW:gA3>%n+TV*Xf'/[ph@6]E4j0T91@(q$X\H;mm6LuZ4WeAD^$Y(/d^!Pt3-Ji*ptXo%\]P'2V^!&?Rj%`qmqGe_)!Xb;s<U"J5=.%+Sc#d"l&Pt@hY9X3_G<jt9Em9"QC"D]%aeiXMFGp$5\5d9B:<pg_J<u\tZ?@oSt"KQf\MP6h6j^hQ]$\m3c(fkE^=K=$sJBO?!P725P$.eY[XQ;&2;quCt<'2hM>/(ZNoc(`9#D+fDQq^MVRl&_qisJ-hRd8BGLSRINfGsY+R?;"Nrn&r(B\B!f((.2*rLf]1bTu6`>)AaPbdmq/<=&f.&dVG_&;?MT))\6;:',$5!l8V_"KI_!Bs"\pA8?:X(VDNI>$;!XZpXsggNlt0f<<G.e=5h]a<lP'J6D>IK%2SNT^%h.<<?%MPc7n#bKRI_D!c\B\5bQf>oY@nq(*L81O60\@q@!j/Q;""n&q9GTGguBZX5@K\d!ofBOFZe^p'hi?qgaPY%m/\oPqS>kSb<3UOb^2"2h,CJi+!2];e.l4mJQ-#)<(i?r5B=YZ;[kY*LDI'CU+*ahY.7'a/;tR<CHkS5&o%\TA"mI_(+s<<L.=#(e]AQu-,P.q>dI7J8;Zh6*<X+m]/TH&c:1XM4Rt9nlpu`)nIsb8N?=j&fFdHetA[',fSXluH"<?'[];@+IJKeic:ln:4[9J2=]j>31_*G%L[BJ6DVs=n_pL_ZON,"4#gGAA^@/\$[3`@Ff4Hm#`*nlZm&[eulKS*;p)p,6Mi81X$]Yp!1u1"(p@BB&,+NO0O=?pAil'@bLXq-[#5f^0Xo7,LsJQin3&\%YJ9c`SQ#Y/&Sgi$4Y&LI>*DC9gb4\kC.o[CEQa.5d7,:?qga>WWiM^%7Om"lb`%S@FPY%W7?T!/IRbC<L(,ZIZLUT0#<tXK%742C2o_:A+t=d4YFQc"9Pt:q]V:&IBsuG@*KP)g8H/s)`@i")DKl8%Q3X#'N:5I=(Qg2OE9O9eO(mI@RhDH^/#"HCg*OU,!iI[Kdqu`Q.J-/3>#D<BBFbQbl_S6"2UE2Ji+',?Lo:\:5[[#W4b+5H<u32]L2tI>?b4oHL$I_)o5]pR.\*[]9]pc0^^$3HX;<3]U8A37^,o4Ohkeu/$s?11@;I]XOW&.YDlq,9G4FP_!I"CeC^B3AJuWU3Z_n%+j24'@S7+a:QDRJXg0pp(%K)5]nqK^Z)St4YZAD$MP(k)<])S4C1,\T"pS':%'(:e$PV;qAu.5`QH$.tQXMai)53u*&T8O(p7ms?2(ld]gq#hsfB6IgPVMMP/q[nDL=NeSG+K(d5"FWgXH"9TeQub<Ns5eg%@GC52Irl2oJ,G)4^G7$'U`^Y]2L(Zfr-:OrDTd;$S.95Yqcl%Zd,g2$<g^LH[&XA/F]I>H,-M.^oVmuXF%aaH[r2EQuZ@gY"oF0Bc+5\&3U)j(A9oB9LfYqVo>`["Mn83&?KYtm,9)uRAb#d9SWks\d-Hj#L%L2O?I>//*-"$&4^T)p"D+c[:.Y`(piW2Z;!+(cRUF)^qB)AA>^Ae?Q:R6if"l3@h=N-a*o(YT.:JSS6!!r37^3n4[!'=Ui?nJCE<c44:nT>+B\5#YjPrG&(c/MNnQ2Q[:.YX@b1.n0n6s]mq<NDhQ@hcf@0L^"%59Q6p3enZ=;Z;l=3(gf$e*Z3t8M[+>N1#+IgsKiLQpRS9:`UJ#C,O@C4=a3MmWs*upb]"k\/+L>CI\k2P#cAN_$_***11?Vguh4WG$73?VFF5d6NrH8oRqN.-1VP1N.,n)&oUnb;p0RLVIGVL8Ht_^iQ(Wm)dSfMD*i-ca^O_-0lk[4sqNDdAGdlB:O=\pqZ_I/qs<!Pt3,Ji*c9>Igh,L2%gpaYIuGJ).mf)(PNi%Z9)G@bYZ(_sWns;QRrfYCfbi.dqLk.YE'2dQZ=MRg4[J%h,K[#hEa#g==o3r(]miD%*JQ0s&qqYh$i'WudW:l=%h!Y\1*sL=Ou`enS\sXc:Gq[4U'iNl(HD_l<rn[FuHuCL],IC+E/)\b!N?db6,C;]E])qNt^A"3#^O:`3FEEI[aIe$s>j?O_(;(]kE[]bCh0QT8el:g)F;rc'(hU8`n"3u,O>j9^#+LB<&b#6\fT0^GPIm94?aLATfg7p^eT]U8@u,?V"+aC)E`*8:)!*CmjVXEhU_f1)uF)j.sOlh,[;/_(Z)oaF7#Q=7OsmJ<-.__8ip?\5]>0^F-QYiYtl;VDEPSc*Uu0=pL[L%rsm"=pm^GP9)DOP7]9q\*f"0@&o(S+eEc#FF%)PRcQoko)6j(*MlT0PYah!*3-kk]NM((Vs9`R.\T-f".2U9`pO04S4Hd,O-=(lPYM%/]1-Cje2#XZgO/fbqHn:D+%^2D&2c`:\RMdjoMgVJRH_)_3tVMf$m%?k8h*mpT#5`c*"a@B@nuWfR$nYQ#l].]8L*EhD[]=m&9dc#`a$<o<"2IJ6DVs=ndGLY77gEgNq=f[:.X%_lVIE=mokE\d-HJLOD[2]AA#!*XL=P&?KNcDj=Q3bZ\?bXQRR(k:Q.@EHs;)Gqj)ELL=uc&?L5/m!d8ePTfb]\/K\Bhq/IjbO1?uB+',7Na3mZPpoc5I$!/"a6")a^8=7c0F_aR.1=ukW#>rm1_?a&^oWej2!_"*)gec43?G#*ek:XspDUkOBfL^2[dQSt^6A"6%C.#U2sV&j$2OG?N?\L:I5!q[Y_uX7%7fp9p*ok24aT\5R.Z#_=k<Z,6N^pp"$iK^^Apsl+[Q~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000817 00000 n
0000000876 00000 n
trailer
<<
/ID
[<8c464eebdaef11a4ea517fa6232db978><8c464eebdaef11a4ea517fa6232db978>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
4682
%%EOF

View File

@@ -0,0 +1,583 @@
{
"page_format": "A0",
"orientation": "portrait",
"page_size_mm": {
"width": 841.0,
"height": 1189.0
},
"seed": 103,
"num_arucos": 40,
"aruco_size_mm": 25.0,
"aruco_dictionary": "DICT_4X4_250",
"aruco_start_id": 103,
"page_border_margin_mm": 50.0,
"forbidden_rectangle_mm": {
"x": 318.5,
"y": 94.5,
"w": 204.0,
"h": 1000.0
},
"forbidden_rectangle_margin_mm": 30.0,
"placements": [
{
"id": 103,
"position": [
-453.5,
-331.0,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 104,
"position": [
-749.92,
-144.69,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 105,
"position": [
-51.03,
216.13,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 106,
"position": [
-94.29,
-254.46,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 107,
"position": [
-204.73,
311.12,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 108,
"position": [
5.69,
-222.91,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 109,
"position": [
-82.1,
208.77,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 110,
"position": [
-524.92,
278.61,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 111,
"position": [
-403.65,
238.66,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 112,
"position": [
-360.75,
191.8,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 113,
"position": [
-267.34,
190.31,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 114,
"position": [
-1017.49,
215.5,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 115,
"position": [
-919.81,
-264.8,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 116,
"position": [
-480.39,
157.71,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 117,
"position": [
-415.03,
265.79,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 118,
"position": [
-70.87,
-288.32,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 119,
"position": [
-712.35,
-212.06,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 120,
"position": [
-484.2,
305.52,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 121,
"position": [
-1018.02,
-343.04,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 122,
"position": [
-278.45,
-137.41,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 123,
"position": [
-441.36,
323.68,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 124,
"position": [
-656.23,
338.18,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 125,
"position": [
-546.24,
-254.82,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 126,
"position": [
-354.69,
239.76,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 127,
"position": [
-693.84,
-305.9,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 128,
"position": [
-906.09,
349.86,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 129,
"position": [
-272.56,
-252.41,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 130,
"position": [
-415.2,
-198.86,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 131,
"position": [
-792.1,
272.52,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 132,
"position": [
-692.76,
-270.84,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 133,
"position": [
22.14,
-298.6,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 134,
"position": [
-245.52,
-246.37,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 135,
"position": [
-1030.98,
361.6,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 136,
"position": [
-553.56,
-190.0,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 137,
"position": [
-23.73,
289.53,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 138,
"position": [
-531.23,
-298.84,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 139,
"position": [
-801.83,
-245.79,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 140,
"position": [
-824.79,
-167.79,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 141,
"position": [
-229.87,
188.58,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 142,
"position": [
-773.93,
-198.68,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,863 @@
{
"page_format": "A0",
"orientation": "portrait",
"page_size_mm": {
"width": 841.0,
"height": 1189.0
},
"seed": 223,
"num_arucos": 60,
"aruco_size_mm": 25.0,
"aruco_dictionary": "DICT_4X4_250",
"aruco_start_id": 103,
"page_border_margin_mm": 50.0,
"forbidden_rectangle_mm": {
"x": 318.5,
"y": 94.5,
"w": 204.0,
"h": 1000.0
},
"forbidden_rectangle_margin_mm": 30.0,
"placements": [
{
"id": 103,
"position": [
-536.71,
-185.44,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 104,
"position": [
-344.23,
286.54,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 105,
"position": [
-688.69,
320.72,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 106,
"position": [
-1006.0,
-158.33,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 107,
"position": [
-573.41,
-211.86,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 108,
"position": [
-167.8,
172.08,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 109,
"position": [
-94.68,
-208.66,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 110,
"position": [
-486.25,
-212.24,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 111,
"position": [
-342.27,
330.59,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 112,
"position": [
-283.72,
262.58,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 113,
"position": [
-498.68,
-168.67,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 114,
"position": [
-602.86,
364.05,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 115,
"position": [
-50.09,
218.11,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 116,
"position": [
-626.21,
278.75,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 117,
"position": [
-434.36,
-283.81,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 118,
"position": [
22.42,
-335.83,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 119,
"position": [
-404.7,
175.1,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 120,
"position": [
-777.4,
236.15,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 121,
"position": [
21.27,
188.23,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 122,
"position": [
-803.39,
297.37,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 123,
"position": [
-209.75,
363.23,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 124,
"position": [
-523.07,
-267.04,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 125,
"position": [
-573.73,
-170.64,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 126,
"position": [
-7.61,
281.21,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 127,
"position": [
-601.87,
-300.33,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 128,
"position": [
-749.75,
284.01,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 129,
"position": [
-440.99,
-194.32,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 130,
"position": [
-221.73,
-333.11,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 131,
"position": [
-93.78,
-144.5,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 132,
"position": [
25.7,
-194.58,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 133,
"position": [
-685.21,
-166.8,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 134,
"position": [
-18.19,
-191.57,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 135,
"position": [
-823.11,
344.38,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 136,
"position": [
-312.3,
159.11,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 137,
"position": [
-863.59,
335.92,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 138,
"position": [
-132.14,
-169.03,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 139,
"position": [
-219.16,
-297.24,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 140,
"position": [
-44.16,
-339.22,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 141,
"position": [
-407.49,
-258.42,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 142,
"position": [
-504.58,
312.75,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 143,
"position": [
-362.89,
-292.01,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 144,
"position": [
-943.63,
245.76,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 145,
"position": [
-765.87,
-316.04,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 146,
"position": [
-988.02,
369.14,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 147,
"position": [
-643.17,
-316.43,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 148,
"position": [
-723.35,
-328.05,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 149,
"position": [
-645.09,
184.84,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 150,
"position": [
-934.88,
-143.6,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 151,
"position": [
-875.7,
-173.65,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 152,
"position": [
-186.04,
274.07,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 153,
"position": [
-369.77,
186.49,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 154,
"position": [
-304.35,
359.67,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 155,
"position": [
-575.27,
-315.06,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 156,
"position": [
-959.16,
321.55,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 157,
"position": [
-803.25,
-172.36,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 158,
"position": [
-117.7,
-298.66,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 159,
"position": [
-649.69,
223.0,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 160,
"position": [
-105.71,
187.71,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 161,
"position": [
-826.71,
-239.16,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
},
{
"id": 162,
"position": [
-524.84,
266.25,
-19
],
"normal": [
0,
0,
1
],
"spin": 90
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,374 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import random
from dataclasses import dataclass
from pathlib import Path
from typing import List, Tuple
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
try:
import cv2
except ImportError as exc:
raise SystemExit(
"OpenCV ist erforderlich. Installiere es mit: pip install opencv-contrib-python"
) from exc
# =========================
# Header / Parameter
# =========================
PAGE_FORMAT = "A0" # z.B. A0, A1, A2, A3, A4
ORIENTATION = "portrait" # "portrait" oder "landscape"
NUM_ARUCOS = 60
ARUCO_SIZE_MM = 25.0
ARUCO_START_ID = 103 # erster Marker aus DICT_4X4_250
SEED = 223 # Zufalls-Seed für reproduzierbare Verteilung
PAGE_BORDER_MARGIN_MM = 50.0 # Abstand aller Marker vom Seitenrand
FORBIDDEN_RECT_W_MM = 204.0
FORBIDDEN_RECT_H_MM = 1000.0
FORBIDDEN_RECT_MARGIN_MM = 30.0 # keine ArUcos innerhalb dieses Abstands
LINE_WIDTH_MM = 1.0 # Linienstärke des Rechtecks
OUTPUT_BASENAME = f"A0_{NUM_ARUCOS}Arucos_{int(ARUCO_SIZE_MM)}mm_Seet{SEED}"
# =========================
# DIN-Formate
# =========================
DIN_SIZES_MM = {
"A0": (841.0, 1189.0),
"A1": (594.0, 841.0),
"A2": (420.0, 594.0),
"A3": (297.0, 420.0),
"A4": (210.0, 297.0),
}
@dataclass(frozen=True)
class RectMM:
x: float
y: float
w: float
h: float
def intersects(self, other: "RectMM") -> bool:
return not (
self.x + self.w <= other.x
or other.x + other.w <= self.x
or self.y + self.h <= other.y
or other.y + other.h <= self.y
)
def mm_to_pt(value_mm: float) -> float:
return value_mm * mm
def get_page_size_mm(page_format: str, orientation: str) -> Tuple[float, float]:
if page_format not in DIN_SIZES_MM:
raise ValueError(f"Unbekanntes Format: {page_format}. Unterstützt: {sorted(DIN_SIZES_MM)}")
w_mm, h_mm = DIN_SIZES_MM[page_format]
if orientation.lower() == "portrait":
return w_mm, h_mm
if orientation.lower() == "landscape":
return h_mm, w_mm
raise ValueError("ORIENTATION muss 'portrait' oder 'landscape' sein.")
def centered_rect(page_w_mm: float, page_h_mm: float, rect_w_mm: float, rect_h_mm: float) -> RectMM:
return RectMM(
x=(page_w_mm - rect_w_mm) / 2.0,
y=(page_h_mm - rect_h_mm) / 2.0,
w=rect_w_mm,
h=rect_h_mm,
)
def expand_rect(rect: RectMM, margin_mm: float) -> RectMM:
return RectMM(
x=rect.x - margin_mm,
y=rect.y - margin_mm,
w=rect.w + 2.0 * margin_mm,
h=rect.h + 2.0 * margin_mm,
)
def get_aruco_dictionary():
# DICT_4X4_250 hat IDs 0..249
return cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_250)
def marker_module_pattern(marker_id: int) -> List[List[int]]:
"""
Liefert ein 6x6-Raster (inkl. schwarzem Rand).
1 = schwarz, 0 = weiss
"""
aruco_dict = get_aruco_dictionary()
img = cv2.aruco.generateImageMarker(aruco_dict, marker_id, 600) # nur zum Abtasten
modules = 6
cell = img.shape[0] // modules
pattern: List[List[int]] = []
for r in range(modules):
row: List[int] = []
for c in range(modules):
cy = int((r + 0.5) * cell)
cx = int((c + 0.5) * cell)
pixel = int(img[cy, cx]) # 0 = schwarz, 255 = weiss
row.append(1 if pixel < 128 else 0)
pattern.append(row)
return pattern
def draw_aruco_vector(
c: canvas.Canvas,
x_mm: float,
y_mm: float,
size_mm: float,
marker_id: int,
page_h_mm: float,
) -> None:
"""
Zeichnet den Marker als Vektor-Rechtecke.
x_mm, y_mm = linke obere Ecke in mm.
"""
pattern = marker_module_pattern(marker_id)
modules = len(pattern)
cell_mm = size_mm / modules
for r in range(modules):
for col in range(modules):
if pattern[r][col] == 1:
cell_x_mm = x_mm + col * cell_mm
cell_y_mm = y_mm + (modules - 1 - r) * cell_mm
c.rect(
mm_to_pt(cell_x_mm),
mm_to_pt(page_h_mm - cell_y_mm - cell_mm),
mm_to_pt(cell_mm),
mm_to_pt(cell_mm),
stroke=0,
fill=1,
)
def place_markers(
page_w_mm: float,
page_h_mm: float,
num_markers: int,
marker_size_mm: float,
border_margin_mm: float,
forbidden_area: RectMM,
forbidden_margin_mm: float,
seed: int,
) -> List[dict]:
rng = random.Random(seed)
placed: List[RectMM] = []
result: List[dict] = []
allowed_x_min = border_margin_mm
allowed_y_min = border_margin_mm
allowed_x_max = page_w_mm - border_margin_mm - marker_size_mm
allowed_y_max = page_h_mm - border_margin_mm - marker_size_mm
if allowed_x_max < allowed_x_min or allowed_y_max < allowed_y_min:
raise RuntimeError("Das Poster ist zu klein für Randabstand und Markergrösse.")
excluded = expand_rect(forbidden_area, forbidden_margin_mm)
attempts = 0
max_attempts = 200000
while len(result) < num_markers and attempts < max_attempts:
attempts += 1
x = rng.uniform(allowed_x_min, allowed_x_max)
y = rng.uniform(allowed_y_min, allowed_y_max)
candidate = RectMM(x=x, y=y, w=marker_size_mm, h=marker_size_mm)
if any(candidate.intersects(other) for other in placed):
continue
if candidate.intersects(excluded):
continue
placed.append(candidate)
result.append(
{
"id": ARUCO_START_ID + len(result),
"x_mm": round(x, 2),
"y_mm": round(y, 2),
"size_mm": marker_size_mm,
}
)
if len(result) < num_markers:
raise RuntimeError(
f"Nicht alle Marker konnten platziert werden: {len(result)} von {num_markers} "
f"(nach {attempts} Versuchen)."
)
return result
def main() -> None:
page_w_mm, page_h_mm = get_page_size_mm(PAGE_FORMAT, ORIENTATION)
if ARUCO_START_ID < 0 or ARUCO_START_ID + NUM_ARUCOS > 250:
raise ValueError("ARUCO_START_ID + NUM_ARUCOS muss in den Bereich 0..249 von DICT_4X4_250 fallen.")
forbidden_rect = centered_rect(page_w_mm, page_h_mm, FORBIDDEN_RECT_W_MM, FORBIDDEN_RECT_H_MM)
# Neues lokales Koordinatensystem
origin_x_mm = forbidden_rect.x + forbidden_rect.w - 90.0
origin_y_mm = forbidden_rect.y
placements = place_markers(
page_w_mm=page_w_mm,
page_h_mm=page_h_mm,
num_markers=NUM_ARUCOS,
marker_size_mm=ARUCO_SIZE_MM,
border_margin_mm=PAGE_BORDER_MARGIN_MM,
forbidden_area=forbidden_rect,
forbidden_margin_mm=FORBIDDEN_RECT_MARGIN_MM,
seed=SEED,
)
pdf_path = Path(f"{OUTPUT_BASENAME}.pdf")
json_path = Path(f"{OUTPUT_BASENAME}.json")
c = canvas.Canvas(str(pdf_path), pagesize=(mm_to_pt(page_w_mm), mm_to_pt(page_h_mm)))
c.setTitle(pdf_path.stem)
c.setAuthor("OpenAI")
c.setSubject("A0 poster with random ArUco placement")
# Weisser Hintergrund
c.setFillColorRGB(1, 1, 1)
c.rect(0, 0, mm_to_pt(page_w_mm), mm_to_pt(page_h_mm), stroke=0, fill=1)
# Rechteck mit 1 mm schwarzer Linie
c.setStrokeColorRGB(0, 0, 0)
c.setLineWidth(mm_to_pt(LINE_WIDTH_MM))
c.rect(
mm_to_pt(forbidden_rect.x),
mm_to_pt(page_h_mm - forbidden_rect.y - forbidden_rect.h),
mm_to_pt(forbidden_rect.w),
mm_to_pt(forbidden_rect.h),
stroke=1,
fill=0,
)
# Koordinatensystem
ARROW_LEN_MM = 50.0
# X-Achse (rot, nach unten)
c.setStrokeColorRGB(1, 0, 0)
c.setLineWidth(mm_to_pt(1.0))
c.line(
mm_to_pt(origin_x_mm),
mm_to_pt(page_h_mm - origin_y_mm),
mm_to_pt(origin_x_mm),
mm_to_pt(page_h_mm - (origin_y_mm + ARROW_LEN_MM)),
)
# Pfeilspitze
c.line(
mm_to_pt(origin_x_mm),
mm_to_pt(page_h_mm - (origin_y_mm + ARROW_LEN_MM)),
mm_to_pt(origin_x_mm - 4),
mm_to_pt(page_h_mm - (origin_y_mm + ARROW_LEN_MM - 4)),
)
c.line(
mm_to_pt(origin_x_mm),
mm_to_pt(page_h_mm - (origin_y_mm + ARROW_LEN_MM)),
mm_to_pt(origin_x_mm + 4),
mm_to_pt(page_h_mm - (origin_y_mm + ARROW_LEN_MM - 4)),
)
# Y-Achse (grün, nach links)
c.setStrokeColorRGB(0, 0.7, 0)
c.line(
mm_to_pt(origin_x_mm),
mm_to_pt(page_h_mm - origin_y_mm),
mm_to_pt(origin_x_mm - ARROW_LEN_MM),
mm_to_pt(page_h_mm - origin_y_mm),
)
# Pfeilspitze
c.line(
mm_to_pt(origin_x_mm - ARROW_LEN_MM),
mm_to_pt(page_h_mm - origin_y_mm),
mm_to_pt(origin_x_mm - ARROW_LEN_MM + 4),
mm_to_pt(page_h_mm - origin_y_mm - 4),
)
c.line(
mm_to_pt(origin_x_mm - ARROW_LEN_MM),
mm_to_pt(page_h_mm - origin_y_mm),
mm_to_pt(origin_x_mm - ARROW_LEN_MM + 4),
mm_to_pt(page_h_mm - origin_y_mm + 4),
)
c.setStrokeColorRGB(0, 0, 0)
# ArUcos zeichnen
c.setFillColorRGB(0, 0, 0)
for item in placements:
draw_aruco_vector(
c=c,
x_mm=item["x_mm"],
y_mm=item["y_mm"],
size_mm=item["size_mm"],
marker_id=item["id"],
page_h_mm=page_h_mm,
)
c.showPage()
c.save()
# JSON mit Positionen
with json_path.open("w", encoding="utf-8") as f:
json.dump(
{
"page_format": PAGE_FORMAT,
"orientation": ORIENTATION,
"page_size_mm": {"width": page_w_mm, "height": page_h_mm},
"seed": SEED,
"num_arucos": NUM_ARUCOS,
"aruco_size_mm": ARUCO_SIZE_MM,
"aruco_dictionary": "DICT_4X4_250",
"aruco_start_id": ARUCO_START_ID,
"page_border_margin_mm": PAGE_BORDER_MARGIN_MM,
"forbidden_rectangle_mm": {
"x": round(forbidden_rect.x, 2),
"y": round(forbidden_rect.y, 2),
"w": forbidden_rect.w,
"h": forbidden_rect.h,
},
"forbidden_rectangle_margin_mm": FORBIDDEN_RECT_MARGIN_MM,
"placements": [
{"id": p["id"],"position": [-1*round((p["y_mm"] + ARUCO_SIZE_MM / 2) - origin_y_mm, 2), round(origin_x_mm - (p["x_mm"] + ARUCO_SIZE_MM / 2), 2),-19],"normal": [0, 0, 1],"spin": 90}
for p in placements
],
},
f,
indent=2,
ensure_ascii=False,
)
print(f"PDF geschrieben: {pdf_path.resolve()}")
print(f"JSON geschrieben: {json_path.resolve()}")
if __name__ == "__main__":
main()