From deb389abdbdb7c20eecffaf2c9732d9284c0c7e3 Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Sat, 31 Oct 2026 12:18:06 +0100 Subject: [PATCH 1/6] ESS_butterfly: colour-coded MCDISPLAY with solid moderators Display only, no change to the simulated neutrons: - ESS blue (#0099dc, from the ess.eu logo) as base pen colour, dark grey proton-beam arrow. - The 2 m "empty zone" and 5.5 m target-monolith circles (3 rings each) become open, lid-less vertical cylinder walls of 36 polygons. - The butterfly is drawn last: light-blue cold wings, red thermal boxes, yellow emission planes (also the extra TBL cold-wing plane). At --trace=2 the cold and thermal moderators are also shown as closed, shaded solids (new butterfly_prism() polyhedron helper; --trace=1 keeps the outlines only). - The beamline loop now uses its own rotation variables instead of overwriting the Br11..Br22 moderator rotation, which the butterfly outline relies on now that it is drawn after that loop. Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/share/ESS_butterfly-geometry.c | 238 +++++++++++++------- mcstas-comps/sources/ESS_butterfly.comp | 4 +- 2 files changed, 158 insertions(+), 84 deletions(-) diff --git a/mcstas-comps/share/ESS_butterfly-geometry.c b/mcstas-comps/share/ESS_butterfly-geometry.c index c5740e194d..dff3aa675d 100644 --- a/mcstas-comps/share/ESS_butterfly-geometry.c +++ b/mcstas-comps/share/ESS_butterfly-geometry.c @@ -25,6 +25,42 @@ double butterfly_e_z2[]= {-1.8501e-01, -1.2719e-01, -1.8501e-01}; double butterfly_e_x2[]= {3.3156e-05, 5.8985e-02, 1.1701e-01}; #pragma acc declare create(butterfly_e_x2) +/* Solid (--trace=2 only) vertical prism from y=-h to y=h over the closed ring of the n + points (px[ring[i]], pz[ring[i]]), as one polyhedron of triangles: two side-wall triangles + per ring edge, and top / bottom faces as fans over nsub convex sub-polygons, each given in + sub[] as (first ring index, number of vertices), wrapping around the ring */ +void butterfly_prism(int n, int *ring, double *px, double *pz, double h, int nsub, int *sub) +{ + int i, k, s, a, b, c, first=1; + char *json, *p; + if (mcdotrace!=2) return; + /* bounds per vertex / face as in mcdis_polygon; at most 2n side and 2(n+2nsub) cap triangles */ + json = malloc(64 + (size_t)2*n*48 + (size_t)(4*n+4*nsub)*64); + if (!json) { + fprintf(stderr, "Error allocating butterfly_prism polyhedron\n"); + return; + } + p = json + sprintf(json, "{ \"vertices\": ["); + /* vertex i at the top (y=h), vertex n+i at the bottom (y=-h) */ + for (i=0; i<2*n; i++) + p += sprintf(p, "%s[%g, %g, %g]", i ? ", " : "", px[ring[i%n]], i=31 && j<35) || j>=60); + if (thermal != pass) continue; + + rAx = Br11*(butterfly_z[j]-Bcz) + Br12*(butterfly_x[j]-Bcx); + rAz = Br21*(butterfly_z[j]-Bcz) + Br22*(butterfly_x[j]-Bcx); + + rBx = Br11*(butterfly_z[j+1]-Bcz) + Br12*(butterfly_x[j+1]-Bcx); + rBz = Br21*(butterfly_z[j+1]-Bcz) + Br22*(butterfly_x[j+1]-Bcx); + + mcdis_line(rAx, y0, rAz, rBx, y0, rBz); + } + } + } + + /* Draw the "border" between the thermal and cold areas, closing the thermal outline */ + for (y0=-Bdelta_y; y0<2*Bdelta_y; y0+=2*Bdelta_y) { + for (j=0; j<2; j++) { + + rAx = Br11*(butterfly_e_z1[j]-Bcz) + Br12*(butterfly_e_x1[j]-Bcx); + rAz = Br21*(butterfly_e_z1[j]-Bcz) + Br22*(butterfly_e_x1[j]-Bcx); + + rBx = Br11*(butterfly_e_z1[j+1]-Bcz) + Br12*(butterfly_e_x1[j+1]-Bcx); + rBz = Br21*(butterfly_e_z1[j+1]-Bcz) + Br22*(butterfly_e_x1[j+1]-Bcx); + + mcdis_line(rAx, y0, rAz, rBx, y0, rBz); + + rAx = Br11*(butterfly_e_z2[j]-Bcz) + Br12*(butterfly_e_x2[j]-Bcx); + rAz = Br21*(butterfly_e_z2[j]-Bcz) + Br22*(butterfly_e_x2[j]-Bcx); + + rBx = Br11*(butterfly_e_z2[j+1]-Bcz) + Br12*(butterfly_e_x2[j+1]-Bcx); + rBz = Br21*(butterfly_e_z2[j+1]-Bcz) + Br22*(butterfly_e_x2[j+1]-Bcx); + + mcdis_line(rAx, y0, rAz, rBx, y0, rBz); + } + } + + /* Indicate the emission planes of cold/thermal moderator, in yellow */ + mcdis_pencolor("#ffff00"); + for (y0=-Bdelta_y; y0<2*Bdelta_y; y0+=2*Bdelta_y) { + mcdis_dashed_line(BrC1_x, y0, BrC1_z, BrC2_x, y0, BrC2_z, 11); + mcdis_dashed_line(BrC1_x, y0, BrC1_z, BrC3_x, y0, BrC3_z, 11); + mcdis_dashed_line(BrT1_x, y0, BrT1_z, BrT2_x, y0, BrT2_z, 11); + mcdis_dashed_line(BrT1_x, y0, BrT1_z, BrT3_x, y0, BrT3_z, 11); + } + mcdis_dashed_line(BrC1_x, -Bdelta_y, BrC1_z, BrC1_x, Bdelta_y, BrC1_z, 11); + mcdis_dashed_line(BrC2_x, -Bdelta_y, BrC2_z, BrC2_x, Bdelta_y, BrC2_z, 11); + mcdis_dashed_line(BrC3_x, -Bdelta_y, BrC3_z, BrC3_x, Bdelta_y, BrC3_z, 11); + mcdis_dashed_line(BrT1_x, -Bdelta_y, BrT1_z, BrT1_x, Bdelta_y, BrT1_z, 11); + mcdis_dashed_line(BrT2_x, -Bdelta_y, BrT2_z, BrT2_x, Bdelta_y, BrT2_z, 11); + mcdis_dashed_line(BrT3_x, -Bdelta_y, BrT3_z, BrT3_x, Bdelta_y, BrT3_z, 11); } diff --git a/mcstas-comps/sources/ESS_butterfly.comp b/mcstas-comps/sources/ESS_butterfly.comp index cd12c488f3..5f623b44fe 100644 --- a/mcstas-comps/sources/ESS_butterfly.comp +++ b/mcstas-comps/sources/ESS_butterfly.comp @@ -665,10 +665,12 @@ TRACE MCDISPLAY %{ #ifndef OPENACC + mcdis_pencolor ("#0099dc"); /* ESS blue, from the ess.eu logo */ magnify (""); butterfly_geometry (delta_y, jmax, cx, cz, orientation_angle, Beamlines, tx, ty, tz, rC1_x, rC1_z, rC2_x, rC2_z, rC3_x, rC3_z, rT1_x, rT1_z, rT2_x, rT2_z, rT3_x, rT3_z, r11, r12, r21, r22, focus_xw, focus_yh); - if (tbl_mode) { /* second cold wing emission plane */ + if (tbl_mode) { /* second cold wing emission plane, yellow as the others */ + mcdis_pencolor ("#ffff00"); mcdis_dashed_line (rC4_x, -delta_y, rC4_z, rC5_x, -delta_y, rC5_z, 11); mcdis_dashed_line (rC4_x, delta_y, rC4_z, rC5_x, delta_y, rC5_z, 11); mcdis_dashed_line (rC5_x, -delta_y, rC5_z, rC5_x, delta_y, rC5_z, 11); From ac6c8df700e0eb1c6073ce37b0962826c4bfe0df Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Sat, 31 Oct 2026 12:27:52 +0100 Subject: [PATCH 2/6] ESS_butterfly: Arm-style proton-beam arrow below the moderators Draw the proton-beam arrow as a line and a cone (as in Arm) instead of three lines, 13.7 cm below the moderator plane, so the arrowhead tip sits under the moderator centre rather than inside the moderator assembly. Display only. Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/share/ESS_butterfly-geometry.c | 22 ++++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/mcstas-comps/share/ESS_butterfly-geometry.c b/mcstas-comps/share/ESS_butterfly-geometry.c index dff3aa675d..cfba3b36d5 100644 --- a/mcstas-comps/share/ESS_butterfly-geometry.c +++ b/mcstas-comps/share/ESS_butterfly-geometry.c @@ -76,31 +76,25 @@ void butterfly_geometry(double Bdelta_y, int Bjmax, double Bcx, double Bcz, int j; double rAx,rAz,rBx,rBz; - /* Arrow indicating proton beam direction */ + /* Arrow indicating proton beam direction, in dark grey and in the style of Arm: a line + from 6 m upstream and a cone with its tip 13.7 cm below the moderator centre */ double ax,az,bx,bz,bbx,bbz,cBcx,cBcz; + double ay=-0.137, anx, anz, al; az = -0.0925-Bcz; ax = 0.0585-Bcx; bz = -0.0925-Bcz; bx = 0.0585+6-Bcx; - bbx = 0.0585+0.1-Bcx; - bbz = -0.0925+0.03-Bcz; - cBcx = 0.0585+0.1-Bcx; - cBcz = -0.0925-0.03-Bcz; /* rAx,0,rAz is the centre of the moderator */ rAx = Br11*(az) + Br12*(ax); rAz = Br21*(az) + Br22*(ax); rBx = Br11*(bz) + Br12*(bx); rBz = Br21*(bz) + Br22*(bx); - /* Main part of the arrow, in dark grey */ + /* unit vector along the proton beam, from upstream towards the target */ + anx = rAx-rBx; anz = rAz-rBz; al = sqrt(anx*anx+anz*anz); + anx /= al; anz /= al; mcdis_pencolor("#a9a9a9"); - mcdis_line(rAx, 0, rAz, rBx, 0, rBz); - /* Inclined lines for arrow head */ - rBx = Br11*(bbz) + Br12*(bbx); - rBz = Br21*(bbz) + Br22*(bbx); - mcdis_line(rAx, 0, rAz, rBx, 0, rBz); - rBx = Br11*(cBcz) + Br12*(cBcx); - rBz = Br21*(cBcz) + Br22*(cBcx); - mcdis_line(rAx, 0, rAz, rBx, 0, rBz); + mcdis_line(rBx, ay, rBz, rAx-0.05*anx, ay, rAz-0.05*anz); + mcdis_cone(rAx-0.05*anx, ay, rAz-0.05*anz, 0.03, 0.1, anx, 0, anz); mcdis_pencolor("#0099dc"); /* back to ESS blue, as set in ESS_butterfly MCDISPLAY */ /* 120 degree "end of sector" lines */ From 8f90e953c7c3eb8d06daa874a818930814acd71e Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Sat, 31 Oct 2026 12:36:31 +0100 Subject: [PATCH 3/6] ESS_butterfly: ESS emblem as target wheel, cylinder arrow shaft - Proton-beam arrow shaft is now a 1 cm radius cylinder (was a line). - The ESS emblem (digitised from the ess.eu logo-small.svg: 7 contours, 439 points, 437 cap triangles) is drawn in ESS blue where the target wheel sits: at proton-beam level, 2.6 m diameter, 8 cm thick, its rim at the beam impact point and centred downstream of it. Solid at --trace=2, outlines at --trace=1. - butterfly_prism() now builds on a general butterfly_solid() slab writer shared with the emblem; the moderator solids are unchanged. Display only. Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/share/ESS_butterfly-geometry.c | 260 ++++++++++++++++++-- 1 file changed, 237 insertions(+), 23 deletions(-) diff --git a/mcstas-comps/share/ESS_butterfly-geometry.c b/mcstas-comps/share/ESS_butterfly-geometry.c index cfba3b36d5..e6183b5b24 100644 --- a/mcstas-comps/share/ESS_butterfly-geometry.c +++ b/mcstas-comps/share/ESS_butterfly-geometry.c @@ -25,42 +25,252 @@ double butterfly_e_z2[]= {-1.8501e-01, -1.2719e-01, -1.8501e-01}; double butterfly_e_x2[]= {3.3156e-05, 5.8985e-02, 1.1701e-01}; #pragma acc declare create(butterfly_e_x2) -/* Solid (--trace=2 only) vertical prism from y=-h to y=h over the closed ring of the n - points (px[ring[i]], pz[ring[i]]), as one polyhedron of triangles: two side-wall triangles - per ring edge, and top / bottom faces as fans over nsub convex sub-polygons, each given in - sub[] as (first ring index, number of vertices), wrapping around the ring */ -void butterfly_prism(int n, int *ring, double *px, double *pz, double h, int nsub, int *sub) +/* ESS emblem, digitised from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg): + 7 contours (outlines, holes reversed) of 439 points in total, centred and scaled to unit + diameter with u to the right and v up, plus 437 cap triangles (indices into the points) */ +int ess_logo_ncontours = 7; +int ess_logo_contours[] = {0, 90, 90, 101, 191, 87, 278, 89, 367, 9, 376, 25, 401, 38}; +int ess_logo_npoints = 439; +double ess_logo_u[] = { + -0.4661, -0.4416, -0.4251, -0.4064, -0.3857, -0.3631, -0.3168, -0.2601, -0.2027, -0.1435, -0.0783, -0.0180, + 0.0366, 0.0882, 0.1399, 0.1864, 0.2242, 0.2560, 0.2811, 0.2524, 0.2198, 0.1836, 0.1487, 0.1072, + 0.0639, 0.0192, -0.0213, -0.0620, -0.1066, -0.1499, -0.1916, -0.2304, -0.2699, -0.3083, -0.3207, -0.3199, + -0.3148, -0.2853, -0.2478, -0.2103, -0.1744, -0.1365, -0.0925, -0.0471, -0.0009, 0.0445, 0.0887, 0.1312, + 0.1671, 0.2005, 0.2311, 0.2650, 0.2936, 0.2983, 0.3002, 0.2936, 0.2794, 0.2649, 0.2454, 0.2060, + 0.1564, 0.1007, 0.0441, -0.0154, -0.0808, -0.1311, -0.1808, -0.2295, -0.2723, -0.3143, -0.3528, -0.3874, + -0.4209, -0.4490, -0.4692, -0.4845, -0.4939, -0.4975, -0.4945, -0.4828, -0.4745, -0.4698, -0.4676, -0.4786, + -0.4860, -0.4894, -0.4901, -0.4881, -0.4835, -0.4761, 0.2010, 0.1701, 0.1431, 0.1144, 0.0839, 0.0519, + 0.0185, -0.0162, -0.0560, -0.0906, -0.1250, -0.1635, -0.1964, -0.2328, -0.2635, -0.2922, -0.3212, -0.3443, + -0.3678, -0.3884, -0.4037, -0.4182, -0.4280, -0.4351, -0.4398, -0.4408, -0.4384, -0.4324, -0.4241, -0.4125, + -0.3979, -0.3806, -0.3607, -0.3768, -0.3876, -0.3930, -0.3933, -0.3883, -0.3778, -0.3621, -0.3438, -0.3216, + -0.2927, -0.2598, -0.2230, -0.1836, -0.1370, -0.0833, -0.0634, -0.0593, -0.0625, -0.1075, -0.1559, -0.1928, + -0.2276, -0.2599, -0.2925, -0.3209, -0.3448, -0.3634, -0.3766, -0.3843, -0.3863, -0.3839, -0.3770, -0.3605, + -0.3386, -0.3397, -0.3446, -0.3666, -0.3888, -0.4078, -0.4234, -0.4339, -0.4424, -0.4471, -0.4481, -0.4453, + -0.4387, -0.4286, -0.4151, -0.3983, -0.3783, -0.3553, -0.3294, -0.3007, -0.2684, -0.2382, -0.2024, -0.1653, + -0.1272, -0.0885, -0.0543, -0.0150, 0.0245, 0.0579, 0.0942, 0.1327, 0.1645, 0.2042, 0.2045, 0.4178, + 0.3974, 0.3715, 0.3475, 0.3176, 0.2858, 0.2521, 0.2065, 0.2022, 0.2052, 0.2508, 0.2936, 0.3333, + 0.3657, 0.3987, 0.4245, 0.4486, 0.4673, 0.4806, 0.4882, 0.4901, 0.4862, 0.4772, 0.4617, 0.4429, + 0.4169, 0.3789, 0.3379, 0.2925, 0.2398, 0.1838, 0.1299, 0.0750, 0.0158, 0.0604, 0.1039, 0.1456, + 0.1807, 0.2172, 0.2501, 0.2791, 0.3012, 0.3217, 0.3368, 0.3466, 0.3507, 0.3501, 0.3430, 0.3277, + 0.3202, 0.3153, 0.3134, 0.3290, 0.3398, 0.3437, 0.3423, 0.3364, 0.3245, 0.3069, 0.2841, 0.2567, + 0.2251, 0.1897, 0.1553, 0.1189, 0.0808, 0.0315, -0.0189, -0.0213, -0.0218, -0.0000, 0.0348, 0.0651, + 0.1006, 0.1613, 0.2243, 0.2835, 0.3344, 0.3836, 0.4247, 0.4572, 0.4791, 0.4935, 0.4975, 0.4917, + 0.4764, 0.4517, 0.1966, 0.2118, 0.2238, 0.2324, 0.2381, 0.2988, 0.2959, 0.2907, 0.2830, 0.2726, + 0.2449, 0.2268, 0.2067, 0.1662, 0.1348, 0.1098, 0.0919, 0.0819, 0.0703, 0.0431, 0.0244, 0.0020, + -0.0367, -0.0607, -0.0788, -0.0969, -0.1074, -0.1221, -0.1360, -0.1521, -0.1704, -0.2081, -0.2297, -0.2494, + -0.2763, -0.2964, -0.3176, -0.3316, -0.3415, -0.3482, -0.3491, -0.3456, -0.3399, -0.3316, -0.3208, -0.3074, + -0.2926, -0.2763, -0.2587, -0.2397, -0.1993, -0.1675, -0.1350, -0.1090, -0.0947, -0.0777, -0.0529, -0.0267, + 0.0136, 0.0476, 0.0752, 0.0947, 0.1064, 0.1196, 0.1496, 0.1697, 0.1935, 0.2317, 0.2636, 0.2797, + 0.2907, 0.2989, 0.3042, 0.3067, 0.3062, 0.3030, 0.2950, 0.2865, 0.2721, 0.2405, 0.1731, 0.1605, + 0.1524, 0.1494, 0.1477, 0.1488, 0.1544, 0.1646, 0.1737, -0.2085, -0.1892, -0.1688, -0.1534, -0.1437, + -0.2810, -0.2704, -0.2542, -0.2332, -0.0096, -0.0256, -0.0420, -0.0522, -0.0552, -0.1574, -0.1701, -0.1864, + -0.2076, -0.2286, -0.2466, -0.2651, -0.2778, -0.2836, -0.0814, -0.0794, -0.0465, 0.0187, 0.0299, 0.0371, + 0.0407, 0.0411, 0.0343, 0.0236, 0.0140, 0.1787, 0.2106, 0.2266, 0.2365, 0.2377, 0.2309, 0.2157, + 0.1930, 0.1664, 0.1582, 0.1487, 0.1430, 0.1414, 0.1101, 0.1082, 0.1019, 0.0946, 0.0819, 0.0662, + 0.0481, -0.0123, -0.0362, -0.0473, -0.0489, -0.0478, -0.0393, -0.0229, -0.0000, 0.0196, 0.0333, 0.0394, + 0.0414, 0.0829, 0.0850, 0.0909, 0.1007, 0.1142, 0.1415 +}; +double ess_logo_v[] = { + 0.2722, 0.3212, 0.3459, 0.3690, 0.3904, 0.4101, 0.4413, 0.4676, 0.4841, 0.4920, 0.4910, 0.4812, + 0.4648, 0.4420, 0.4105, 0.3724, 0.3318, 0.2869, 0.2386, 0.2737, 0.3051, 0.3323, 0.3527, 0.3709, + 0.3838, 0.3915, 0.3937, 0.3915, 0.3839, 0.3709, 0.3526, 0.3294, 0.2984, 0.2586, 0.2428, 0.2376, + 0.2384, 0.2733, 0.3075, 0.3336, 0.3528, 0.3678, 0.3794, 0.3854, 0.3857, 0.3803, 0.3694, 0.3530, + 0.3340, 0.3112, 0.2849, 0.2476, 0.2059, 0.2044, 0.2063, 0.2276, 0.2597, 0.2863, 0.3161, 0.3634, + 0.4075, 0.4436, 0.4698, 0.4882, 0.4987, 0.5000, 0.4955, 0.4852, 0.4708, 0.4511, 0.4270, 0.3989, + 0.3633, 0.3233, 0.2839, 0.2414, 0.1974, 0.1527, 0.1027, 0.0475, 0.0226, 0.0204, 0.0252, 0.0601, + 0.0951, 0.1252, 0.1552, 0.1850, 0.2145, 0.2436, -0.3658, -0.3977, -0.4212, -0.4415, -0.4585, -0.4722, + -0.4826, -0.4894, -0.4927, -0.4916, -0.4870, -0.4774, -0.4652, -0.4471, -0.4277, -0.4054, -0.3781, -0.3521, + -0.3199, -0.2855, -0.2537, -0.2156, -0.1809, -0.1452, -0.1043, -0.0689, -0.0288, 0.0109, 0.0451, 0.0798, + 0.1131, 0.1448, 0.1745, 0.1321, 0.0882, 0.0432, 0.0029, -0.0422, -0.0862, -0.1287, -0.1649, -0.1990, + -0.2339, -0.2649, -0.2918, -0.3138, -0.3326, -0.3459, -0.3487, -0.3455, -0.3414, -0.3333, -0.3179, -0.3009, + -0.2799, -0.2551, -0.2232, -0.1873, -0.1478, -0.1060, -0.0624, -0.0175, 0.0230, 0.0634, 0.1032, 0.1561, + 0.2017, 0.2065, 0.2060, 0.1790, 0.1455, 0.1096, 0.0716, 0.0372, -0.0027, -0.0432, -0.0842, -0.1255, + -0.1669, -0.2070, -0.2456, -0.2825, -0.3173, -0.3500, -0.3803, -0.4079, -0.4333, -0.4526, -0.4707, -0.4846, + -0.4941, -0.4992, -0.5000, -0.4966, -0.4886, -0.4779, -0.4616, -0.4382, -0.4130, -0.3718, -0.3676, 0.2936, + 0.3159, 0.3394, 0.3573, 0.3754, 0.3904, 0.4024, 0.4131, 0.4101, 0.4059, 0.3951, 0.3790, 0.3577, + 0.3346, 0.3040, 0.2731, 0.2354, 0.1950, 0.1524, 0.1079, 0.0620, 0.0151, -0.0291, -0.0763, -0.1171, + -0.1601, -0.2078, -0.2472, -0.2811, -0.3107, -0.3326, -0.3457, -0.3514, -0.3492, -0.3420, -0.3294, -0.3116, + -0.2916, -0.2648, -0.2337, -0.1988, -0.1649, -0.1239, -0.0813, -0.0372, 0.0031, 0.0483, 0.0981, 0.1512, + 0.1699, 0.1718, 0.1669, 0.1239, 0.0743, 0.0288, -0.0119, -0.0522, -0.0961, -0.1384, -0.1786, -0.2152, + -0.2480, -0.2766, -0.2982, -0.3157, -0.3291, -0.3398, -0.3437, -0.3447, -0.3496, -0.3546, -0.3583, -0.3591, + -0.3571, -0.3466, -0.3257, -0.2955, -0.2596, -0.2135, -0.1617, -0.1051, -0.0495, 0.0139, 0.0754, 0.1350, + 0.1918, 0.2450, 0.1080, 0.1052, 0.0993, 0.0908, 0.0760, 0.0760, 0.0948, 0.1076, 0.1192, 0.1297, + 0.1462, 0.1520, 0.1552, 0.1544, 0.1463, 0.1317, 0.1114, 0.1240, 0.1341, 0.1482, 0.1532, 0.1558, + 0.1534, 0.1468, 0.1376, 0.1221, 0.1062, 0.1221, 0.1333, 0.1427, 0.1498, 0.1559, 0.1553, 0.1521, + 0.1428, 0.1307, 0.1110, 0.0917, 0.0703, 0.0418, 0.0105, -0.0143, -0.0326, -0.0494, -0.0649, -0.0789, + -0.0908, -0.1002, -0.1070, -0.1114, -0.1127, -0.1061, -0.0900, -0.0664, -0.0832, -0.0955, -0.1063, -0.1119, + -0.1127, -0.1061, -0.0932, -0.0759, -0.0871, -0.0960, -0.1080, -0.1119, -0.1134, -0.1096, -0.0981, -0.0875, + -0.0767, -0.0648, -0.0517, -0.0375, -0.0228, -0.0097, 0.0053, 0.0147, 0.0251, 0.0384, 0.0544, 0.0597, + 0.0659, 0.0709, 0.0799, 0.0872, 0.0965, 0.1034, 0.1064, 0.1043, 0.1004, 0.0892, 0.0725, 0.0520, + 0.0520, 0.0736, 0.0907, 0.1015, -0.0643, -0.0617, -0.0536, -0.0411, -0.0294, -0.0294, -0.0465, -0.0566, + -0.0616, -0.0603, -0.0540, -0.0403, -0.0217, 0.0000, 0.0000, 0.0203, 0.0060, -0.0103, -0.0154, -0.0214, + -0.0288, -0.0385, -0.0521, -0.0594, -0.0626, -0.0008, -0.0087, -0.0154, -0.0261, -0.0385, -0.0521, -0.0612, + -0.0645, -0.0601, -0.0560, -0.0478, -0.0374, -0.0294, -0.0294, -0.0157, -0.0001, 0.0098, 0.0210, 0.0300, + 0.0370, 0.0511, 0.0597, 0.0709, 0.0798, 0.0872, 0.0991, 0.1064, 0.1080, 0.1035, 0.0939, 0.0838, + 0.0760, 0.0760, 0.0603, 0.0460, 0.0333, 0.0222, 0.0088 +}; +int ess_logo_ntriangles = 437; +int ess_logo_triangles[] = { + 18, 19, 20, 18, 20, 21, 21, 22, 23, 21, 23, 24, 24, 25, 26, 24, 26, 27, + 27, 28, 29, 27, 29, 30, 30, 31, 32, 32, 33, 34, 32, 34, 35, 32, 35, 36, + 32, 36, 37, 32, 37, 38, 30, 32, 38, 30, 38, 39, 30, 39, 40, 30, 40, 41, + 27, 30, 41, 27, 41, 42, 27, 42, 43, 27, 43, 44, 24, 27, 44, 24, 44, 45, + 24, 45, 46, 24, 46, 47, 21, 24, 47, 21, 47, 48, 21, 48, 49, 21, 49, 50, + 18, 21, 50, 18, 50, 51, 18, 51, 52, 18, 52, 53, 18, 53, 54, 17, 18, 54, + 17, 54, 55, 17, 55, 56, 16, 17, 56, 16, 56, 57, 16, 57, 58, 15, 16, 58, + 15, 58, 59, 14, 15, 59, 14, 59, 60, 13, 14, 60, 13, 60, 61, 12, 13, 61, + 12, 61, 62, 11, 12, 62, 11, 62, 63, 10, 11, 63, 10, 63, 64, 9, 10, 64, + 9, 64, 65, 8, 9, 65, 8, 65, 66, 7, 8, 66, 7, 66, 67, 6, 7, 67, + 6, 67, 68, 6, 68, 69, 5, 6, 69, 5, 69, 70, 4, 5, 70, 3, 4, 70, + 3, 70, 71, 2, 3, 71, 2, 71, 72, 1, 2, 72, 0, 1, 72, 0, 72, 73, + 89, 0, 73, 89, 73, 74, 89, 74, 75, 89, 75, 76, 76, 77, 78, 78, 79, 80, + 78, 80, 81, 78, 81, 82, 78, 82, 83, 78, 83, 84, 78, 84, 85, 76, 78, 85, + 76, 85, 86, 76, 86, 87, 76, 87, 88, 76, 88, 89, 190, 90, 91, 122, 123, 124, + 122, 124, 125, 125, 126, 127, 125, 127, 128, 128, 129, 130, 128, 130, 131, 131, 132, 133, + 131, 133, 134, 134, 135, 136, 136, 137, 138, 136, 138, 139, 136, 139, 140, 136, 140, 141, + 136, 141, 142, 134, 136, 142, 134, 142, 143, 134, 143, 144, 134, 144, 145, 131, 134, 145, + 131, 145, 146, 131, 146, 147, 131, 147, 148, 128, 131, 148, 128, 148, 149, 128, 149, 150, + 128, 150, 151, 125, 128, 151, 125, 151, 152, 125, 152, 153, 125, 153, 154, 122, 125, 154, + 122, 154, 155, 122, 155, 156, 122, 156, 157, 121, 122, 157, 121, 157, 158, 121, 158, 159, + 120, 121, 159, 120, 159, 160, 119, 120, 160, 119, 160, 161, 118, 119, 161, 118, 161, 162, + 117, 118, 162, 116, 117, 162, 116, 162, 163, 116, 163, 164, 115, 116, 164, 115, 164, 165, + 114, 115, 165, 113, 114, 165, 113, 165, 166, 112, 113, 166, 112, 166, 167, 111, 112, 167, + 111, 167, 168, 110, 111, 168, 110, 168, 169, 109, 110, 169, 109, 169, 170, 108, 109, 170, + 108, 170, 171, 107, 108, 171, 107, 171, 172, 106, 107, 172, 106, 172, 173, 105, 106, 173, + 105, 173, 174, 104, 105, 174, 104, 174, 175, 103, 104, 175, 103, 175, 176, 102, 103, 176, + 102, 176, 177, 101, 102, 177, 101, 177, 178, 100, 101, 178, 100, 178, 179, 99, 100, 179, + 99, 179, 180, 98, 99, 180, 98, 180, 181, 97, 98, 181, 97, 181, 182, 96, 97, 182, + 96, 182, 183, 95, 96, 183, 95, 183, 184, 94, 95, 184, 94, 184, 185, 93, 94, 185, + 93, 185, 186, 92, 93, 186, 92, 186, 187, 91, 92, 187, 190, 91, 187, 190, 187, 188, + 188, 189, 190, 277, 191, 192, 277, 192, 193, 193, 194, 195, 193, 195, 196, 193, 196, 197, + 197, 198, 199, 197, 199, 200, 197, 200, 201, 197, 201, 202, 193, 197, 202, 193, 202, 203, + 193, 203, 204, 193, 204, 205, 277, 193, 205, 277, 205, 206, 277, 206, 207, 277, 207, 208, + 224, 225, 226, 224, 226, 227, 227, 228, 229, 227, 229, 230, 230, 231, 232, 230, 232, 233, + 233, 234, 235, 233, 235, 236, 236, 237, 238, 238, 239, 240, 238, 240, 241, 238, 241, 242, + 238, 242, 243, 238, 243, 244, 236, 238, 244, 236, 244, 245, 236, 245, 246, 236, 246, 247, + 233, 236, 247, 233, 247, 248, 233, 248, 249, 233, 249, 250, 230, 233, 250, 230, 250, 251, + 230, 251, 252, 230, 252, 253, 227, 230, 253, 227, 253, 254, 227, 254, 255, 227, 255, 256, + 224, 227, 256, 224, 256, 257, 224, 257, 258, 224, 258, 259, 224, 259, 260, 223, 224, 260, + 223, 260, 261, 223, 261, 262, 222, 223, 262, 222, 262, 263, 222, 263, 264, 221, 222, 264, + 221, 264, 265, 220, 221, 265, 220, 265, 266, 219, 220, 266, 219, 266, 267, 218, 219, 267, + 218, 267, 268, 217, 218, 268, 217, 268, 269, 216, 217, 269, 216, 269, 270, 215, 216, 270, + 214, 215, 270, 214, 270, 271, 213, 214, 271, 213, 271, 272, 212, 213, 272, 212, 272, 273, + 211, 212, 273, 210, 211, 273, 210, 273, 274, 209, 210, 274, 209, 274, 275, 208, 209, 275, + 277, 208, 275, 275, 276, 277, 281, 282, 283, 280, 281, 283, 279, 280, 283, 279, 283, 284, + 278, 279, 284, 278, 284, 285, 278, 285, 286, 366, 278, 286, 366, 286, 287, 366, 287, 288, + 366, 288, 289, 366, 289, 290, 366, 290, 291, 366, 291, 292, 366, 292, 293, 366, 293, 294, + 294, 295, 296, 294, 296, 297, 294, 297, 298, 294, 298, 299, 294, 299, 300, 294, 300, 301, + 294, 301, 302, 294, 302, 303, 366, 294, 303, 370, 371, 304, 369, 370, 304, 368, 369, 304, + 367, 368, 304, 367, 304, 305, 375, 367, 305, 375, 305, 306, 375, 306, 307, 375, 307, 308, + 374, 375, 308, 374, 308, 309, 373, 374, 309, 373, 309, 310, 373, 310, 311, 372, 373, 311, + 372, 311, 312, 372, 312, 313, 372, 313, 314, 372, 314, 315, 372, 315, 316, 372, 316, 317, + 371, 372, 317, 371, 317, 318, 371, 318, 319, 319, 320, 321, 319, 321, 322, 319, 322, 323, + 319, 323, 324, 319, 324, 325, 319, 325, 326, 319, 326, 327, 319, 327, 328, 319, 328, 329, + 329, 330, 331, 331, 332, 333, 331, 333, 334, 331, 334, 335, 331, 335, 336, 331, 336, 337, + 331, 337, 338, 338, 397, 398, 338, 398, 399, 338, 399, 400, 338, 400, 376, 331, 338, 376, + 331, 376, 377, 331, 377, 378, 331, 378, 379, 331, 379, 380, 329, 331, 380, 329, 380, 381, + 329, 381, 382, 329, 382, 383, 329, 383, 384, 329, 384, 385, 329, 385, 386, 319, 329, 386, + 319, 386, 387, 319, 387, 388, 319, 388, 389, 371, 319, 389, 371, 389, 390, 304, 371, 390, + 303, 304, 390, 303, 390, 391, 303, 391, 392, 303, 392, 393, 303, 393, 394, 303, 394, 395, + 396, 397, 338, 395, 396, 338, 395, 338, 339, 303, 395, 339, 339, 340, 341, 339, 341, 342, + 339, 342, 343, 339, 343, 344, 339, 344, 345, 339, 345, 346, 339, 346, 347, 347, 405, 406, + 347, 406, 407, 347, 407, 408, 339, 347, 408, 339, 408, 409, 339, 409, 410, 339, 410, 411, + 339, 411, 412, 339, 412, 413, 339, 413, 414, 339, 414, 415, 339, 415, 416, 339, 416, 417, + 339, 417, 418, 339, 418, 419, 339, 419, 420, 339, 420, 421, 339, 421, 422, 339, 422, 423, + 303, 339, 423, 303, 423, 424, 303, 424, 425, 303, 425, 426, 303, 426, 427, 303, 427, 428, + 366, 303, 428, 366, 428, 429, 366, 429, 430, 366, 430, 431, 366, 431, 432, 433, 434, 435, + 433, 435, 436, 433, 436, 437, 433, 437, 438, 433, 438, 401, 433, 401, 402, 433, 402, 403, + 404, 405, 347, 404, 347, 348, 403, 404, 348, 403, 348, 349, 403, 349, 350, 433, 403, 350, + 433, 350, 351, 433, 351, 352, 433, 352, 353, 433, 353, 354, 433, 354, 355, 433, 355, 356, + 356, 357, 358, 433, 356, 358, 433, 358, 359, 433, 359, 360, 433, 360, 361, 433, 361, 362, + 432, 433, 362, 432, 362, 363, 432, 363, 364, 432, 364, 365, 432, 365, 366 +}; + +/* Solid (--trace=2 only) vertical slab from y=ybot to y=ytop over the n points (px[i], pz[i]), + as one polyhedron of triangles: two side-wall triangles per edge of the ncont closed contours, + given in cont[] as (first point, number of points) pairs (holes running opposite to outlines), + and the ntri cap triangles tri[] (point indices, counter-clockwise seen from above) at the + top and, reversed, at the bottom */ +void butterfly_solid(int n, double *px, double *pz, double ytop, double ybot, int ncont, int *cont, int ntri, int *tri) { - int i, k, s, a, b, c, first=1; + int i, j, k, c; char *json, *p; if (mcdotrace!=2) return; - /* bounds per vertex / face as in mcdis_polygon; at most 2n side and 2(n+2nsub) cap triangles */ - json = malloc(64 + (size_t)2*n*48 + (size_t)(4*n+4*nsub)*64); + /* bounds per vertex / face as in mcdis_polygon */ + json = malloc(64 + (size_t)2*n*48 + (size_t)(2*n+2*ntri)*64); if (!json) { - fprintf(stderr, "Error allocating butterfly_prism polyhedron\n"); + fprintf(stderr, "Error allocating butterfly_solid polyhedron\n"); return; } p = json + sprintf(json, "{ \"vertices\": ["); - /* vertex i at the top (y=h), vertex n+i at the bottom (y=-h) */ + /* vertex i at the top, vertex n+i at the bottom */ for (i=0; i<2*n; i++) - p += sprintf(p, "%s[%g, %g, %g]", i ? ", " : "", px[ring[i%n]], i64) return; + for (i=0; i Date: Sat, 31 Oct 2026 12:47:03 +0100 Subject: [PATCH 4/6] ESS_butterfly: logo parameter, emblem nearer the arrow, digitiser script - New setting parameter logo=1: 0 suppresses the ESS emblem in MCDISPLAY. - Emblem centre moved from 1.3 m to 1.138 m downstream of the beam impact point, so the proton-beam arrowhead sits inside the nearest swoosh with a 2 cm margin (2.5 cm measured clearance in all sectors). - Add share/ESS_butterfly-logo.py, which digitises the ess.eu emblem SVG and rewrites the generated block (now between BEGIN/END markers) in ESS_butterfly-geometry.c, with --dry-run and --preview. Re-running it reproduces the committed data exactly. Display only. Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/share/ESS_butterfly-geometry.c | 15 +- mcstas-comps/share/ESS_butterfly-logo.py | 272 ++++++++++++++++++++ mcstas-comps/sources/ESS_butterfly.comp | 5 +- 3 files changed, 285 insertions(+), 7 deletions(-) create mode 100755 mcstas-comps/share/ESS_butterfly-logo.py diff --git a/mcstas-comps/share/ESS_butterfly-geometry.c b/mcstas-comps/share/ESS_butterfly-geometry.c index e6183b5b24..d7dd5a9b28 100644 --- a/mcstas-comps/share/ESS_butterfly-geometry.c +++ b/mcstas-comps/share/ESS_butterfly-geometry.c @@ -25,8 +25,9 @@ double butterfly_e_z2[]= {-1.8501e-01, -1.2719e-01, -1.8501e-01}; double butterfly_e_x2[]= {3.3156e-05, 5.8985e-02, 1.1701e-01}; #pragma acc declare create(butterfly_e_x2) -/* ESS emblem, digitised from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg): - 7 contours (outlines, holes reversed) of 439 points in total, centred and scaled to unit +/* BEGIN generated ESS logo data (by ESS_butterfly-logo.py, do not edit by hand): + ESS emblem from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg), 7 contours + (holes running opposite to outlines) of 439 points in total, centred and scaled to unit diameter with u to the right and v up, plus 437 cap triangles (indices into the points) */ int ess_logo_ncontours = 7; int ess_logo_contours[] = {0, 90, 90, 101, 191, 87, 278, 89, 367, 9, 376, 25, 401, 38}; @@ -185,6 +186,7 @@ int ess_logo_triangles[] = { 356, 357, 358, 433, 356, 358, 433, 358, 359, 433, 359, 360, 433, 360, 361, 433, 361, 362, 432, 433, 362, 432, 362, 363, 432, 363, 364, 432, 364, 365, 432, 365, 366 }; +/* END generated ESS logo data */ /* Solid (--trace=2 only) vertical slab from y=ybot to y=ytop over the n points (px[i], pz[i]), as one polyhedron of triangles: two side-wall triangles per edge of the ncont closed contours, @@ -280,7 +282,7 @@ void butterfly_geometry(double Bdelta_y, int Bjmax, double Bcx, double Bcz, double BrT2_x, double BrT2_z, double BrT3_x, double BrT3_z, double Br11, double Br12, double Br21, double Br22, - double Bfoc_XW, double Bfoc_YH) + double Bfoc_XW, double Bfoc_YH, int Blogo) { double y0; int j; @@ -308,8 +310,11 @@ void butterfly_geometry(double Bdelta_y, int Bjmax, double Bcx, double Bcz, mcdis_pencolor("#0099dc"); /* back to ESS blue, as set in ESS_butterfly MCDISPLAY */ /* ESS emblem in place of the target wheel (2.6 m diameter, 8 cm tungsten bricks): at the - proton-beam level, its rim at the beam impact point, centred downstream of it */ - butterfly_ess_logo(rAx+1.3*anx, ay, rAz+1.3*anz, 2.6, 0.08, anx, anz); + proton-beam level, centred downstream of the beam impact point such that the arrowhead + sits inside the nearest swoosh, whose inner edge is at most 1.258 m from the emblem centre + within the 3 cm cone radius: 1.258 - 0.02 margin - 0.1 cone length = 1.138 m */ + if (Blogo) + butterfly_ess_logo(rAx+1.138*anx, ay, rAz+1.138*anz, 2.6, 0.08, anx, anz); /* 120 degree "end of sector" lines */ bbz = 2 * cos(DEG2RAD*61); diff --git a/mcstas-comps/share/ESS_butterfly-logo.py b/mcstas-comps/share/ESS_butterfly-logo.py new file mode 100755 index 0000000000..3aa2f8f18a --- /dev/null +++ b/mcstas-comps/share/ESS_butterfly-logo.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +"""Digitise the ESS emblem for the ESS_butterfly MCDISPLAY (target wheel). + +Flattens the emblem outline of the ess.eu logo (https://ess.eu/themes/custom/ess/graphics/logo-small.svg) +into polygon contours, triangulates the filled area (ear clipping, holes bridged in) and writes the +result as C arrays into ESS_butterfly-geometry.c, between the "BEGIN/END generated ESS logo data" +markers. Coordinates are centred on the emblem and scaled to unit diameter (largest extent 1), +u to the right and v upwards (SVG y flipped). + +Usage: python3 ESS_butterfly-logo.py logo-small.svg ESS_butterfly-geometry.c [--dry-run] + [--tolerance 0.25] [--preview preview.png] +--dry-run reports what would change without writing; --preview (needs matplotlib) draws the result. +""" +import argparse +import math +import re +import sys + +BEGIN = "/* BEGIN generated ESS logo data" +END = "/* END generated ESS logo data */" + + +def parse_path(d): + toks = re.findall(r'[MmCcHhVvLlSsZz]|-?(?:\d+\.?\d*|\.\d+)(?:e-?\d+)?', d) + i, cmd = 0, None + cur = (0.0, 0.0) + start = cur + paths, pts = [], [] + + def num(): + nonlocal i + v = float(toks[i]); i += 1 + return v + + while i < len(toks): + if re.match(r'[A-Za-z]', toks[i]): + cmd = toks[i]; i += 1 + if cmd in 'Mm': + x, y = num(), num() + if cmd == 'm': + x += cur[0]; y += cur[1] + if pts: + paths.append(pts) + cur = start = (x, y); pts = [cur] + cmd = 'L' if cmd == 'M' else 'l' + elif cmd in 'Ll': + x, y = num(), num() + if cmd == 'l': + x += cur[0]; y += cur[1] + cur = (x, y); pts.append(cur) + elif cmd in 'Hh': + x = num() + cur = (x + (cur[0] if cmd == 'h' else 0), cur[1]); pts.append(cur) + elif cmd in 'Vv': + y = num() + cur = (cur[0], y + (cur[1] if cmd == 'v' else 0)); pts.append(cur) + elif cmd in 'Cc': + c = [num() for _ in range(6)] + if cmd == 'c': + c = [c[k] + cur[k % 2] for k in range(6)] + p0, p1, p2, p3 = cur, (c[0], c[1]), (c[2], c[3]), (c[4], c[5]) + n = max(2, int(math.ceil((math.dist(p0, p1) + math.dist(p1, p2) + math.dist(p2, p3)) / 1.0))) + for k in range(1, n + 1): + t = k / n; a = 1 - t + pts.append((a**3*p0[0] + 3*a*a*t*p1[0] + 3*a*t*t*p2[0] + t**3*p3[0], + a**3*p0[1] + 3*a*a*t*p1[1] + 3*a*t*t*p2[1] + t**3*p3[1])) + cur = p3 + elif cmd in 'Zz': + cur = start + else: + raise ValueError(cmd) + if pts: + paths.append(pts) + return paths + + +def dp(pts, tol): + """Douglas-Peucker on an open polyline""" + if len(pts) < 3: + return pts + a, b = pts[0], pts[-1] + L = math.dist(a, b) or 1e-12 + dmax, imax = 0, 0 + for k in range(1, len(pts) - 1): + p = pts[k] + dd = abs((b[0]-a[0])*(a[1]-p[1]) - (a[0]-p[0])*(b[1]-a[1])) / L + if dd > dmax: + dmax, imax = dd, k + if dmax <= tol: + return [a, b] + return dp(pts[:imax+1], tol)[:-1] + dp(pts[imax:], tol) + + +def simplify_ring(r, tol): + r = [p for k, p in enumerate(r) if math.dist(p, r[k-1]) > 1e-9] + # split at the point farthest from r[0] so DP keeps both ends + far = max(range(len(r)), key=lambda k: math.dist(r[0], r[k])) + out = dp(r[:far+1], tol)[:-1] + dp(r[far:] + [r[0]], tol)[:-1] + return out + + +def area(r): + return 0.5 * sum(r[k-1][0]*r[k][1] - r[k][0]*r[k-1][1] for k in range(len(r))) + + +def inside(p, r): + c = False + for k in range(len(r)): + a, b = r[k-1], r[k] + if (a[1] > p[1]) != (b[1] > p[1]) and p[0] < a[0] + (p[1]-a[1])*(b[0]-a[0])/(b[1]-a[1]): + c = not c + return c + + +def cross(o, a, b): + return (a[0]-o[0])*(b[1]-o[1]) - (a[1]-o[1])*(b[0]-o[0]) + + +def seg_intersect(p1, p2, q1, q2): + d1, d2 = cross(q1, q2, p1), cross(q1, q2, p2) + d3, d4 = cross(p1, p2, q1), cross(p1, p2, q2) + return (d1*d2 < 0) and (d3*d4 < 0) + + +def bridge(outer, hole, V): + """merge hole (CW) into outer (CCW), both index lists into V, via a mutually visible pair""" + hi = max(range(len(hole)), key=lambda k: V[hole[k]][0]) + hp = V[hole[hi]] + best = None + for oi in sorted(range(len(outer)), key=lambda k: math.dist(V[outer[k]], hp)): + op = V[outer[oi]] + ok = True + for ring in (outer, hole): + for k in range(len(ring)): + a, b = V[ring[k-1]], V[ring[k]] + if seg_intersect(hp, op, a, b): + ok = False; break + if not ok: + break + if ok: + best = oi; break + assert best is not None + h = hole[hi:] + hole[:hi] + return outer[:best+1] + h + [h[0], outer[best]] + outer[best+1:] + + +def earclip(poly, V): + idx = list(poly) + tris = [] + guard = 0 + while len(idx) > 3 and guard < 100000: + guard += 1 + n = len(idx) + cut = False + for k in range(n): + a, b, c = idx[k-1], idx[k], idx[(k+1) % n] + A, B, C = V[a], V[b], V[c] + if cross(A, B, C) <= 1e-12: + continue + ok = True + for j in idx: + if j in (a, b, c): + continue + P = V[j] + if P in (A, B, C): + continue + if cross(A, B, P) >= 0 and cross(B, C, P) >= 0 and cross(C, A, P) >= 0: + ok = False; break + if ok: + tris.append((a, b, c)); del idx[k]; cut = True; break + if not cut: + # degenerate remainder: drop a collinear vertex + for k in range(len(idx)): + if abs(cross(V[idx[k-1]], V[idx[k]], V[idx[(k+1) % len(idx)]])) < 1e-12: + del idx[k]; cut = True; break + if not cut: + raise RuntimeError("ear clipping stuck with %d vertices" % len(idx)) + if len(idx) == 3 and cross(V[idx[0]], V[idx[1]], V[idx[2]]) > 1e-12: + tris.append(tuple(idx)) + return tris + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("svg") + ap.add_argument("geometry_c") + ap.add_argument("--tolerance", type=float, default=0.25, help="curve tolerance in SVG units") + ap.add_argument("--dry-run", action="store_true") + ap.add_argument("--preview") + args = ap.parse_args() + s = open(args.svg, encoding="utf-8").read() + d = re.search(r'\sd="([^"]*)"', s, re.S).group(1) + rings = [simplify_ring(r, args.tolerance) for r in parse_path(d)] + # unit diameter, centred, v up + xs = [p[0] for r in rings for p in r]; ys = [p[1] for r in rings for p in r] + cx, cy = (min(xs)+max(xs))/2, (min(ys)+max(ys))/2 + D = max(max(xs)-min(xs), max(ys)-min(ys)) + rings = [[((x-cx)/D, -(y-cy)/D) for x, y in r] for r in rings] + # holes: odd nesting depth; orient outers CCW, holes CW + depth = [sum(inside(r[0], q) for j, q in enumerate(rings) if j != i) for i, r in enumerate(rings)] + for i, r in enumerate(rings): + if (area(r) > 0) == (depth[i] % 2 == 1): + r.reverse() + V, contours = [], [] + for r in rings: + contours.append((len(V), len(r))); V.extend(r) + rid = [list(range(st, st+n)) for st, n in contours] + tris = [] + for i in range(len(rings)): + if depth[i] % 2: + continue + poly = rid[i] + holes = [j for j in range(len(rings)) if depth[j] == depth[i]+1 and inside(rings[j][0], rings[i])] + for j in sorted(holes, key=lambda j: -max(p[0] for p in rings[j])): + poly = bridge(poly, rid[j], V) + tris += earclip(poly, V) + A_rings = sum(area(r) for r in rings) + A_tris = sum(0.5*cross(V[a], V[b], V[c]) for a, b, c in tris) + print("contours %d (nesting %s), points %d, triangles %d, area contours %.6f triangles %.6f" + % (len(rings), depth, len(V), len(tris), A_rings, A_tris)) + if abs(A_rings - A_tris) > 1e-6 * max(1, abs(A_rings)) or not all(0.5*cross(V[a], V[b], V[c]) > 0 for a, b, c in tris): + sys.exit("Error: triangulation does not cover the emblem exactly, nothing written") + + out = [BEGIN + " (by ESS_butterfly-logo.py, do not edit by hand):", + " ESS emblem from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg), %d contours" + % len(rings), + " (holes running opposite to outlines) of %d points in total, centred and scaled to unit" % len(V), + " diameter with u to the right and v up, plus %d cap triangles (indices into the points) */" + % len(tris), + "int ess_logo_ncontours = %d;" % len(rings), + "int ess_logo_contours[] = {%s};" % ", ".join("%d, %d" % c for c in contours), + "int ess_logo_npoints = %d;" % len(V)] + for name, k in (("u", 0), ("v", 1)): + vals = ["%.4f" % p[k] for p in V] + out.append("double ess_logo_%s[] = {" % name) + out += [" " + ", ".join(vals[j:j+12]) + ("," if j+12 < len(vals) else "") for j in range(0, len(vals), 12)] + out.append("};") + out.append("int ess_logo_ntriangles = %d;" % len(tris)) + out.append("int ess_logo_triangles[] = {") + flat = ["%d" % i for t in tris for i in t] + out += [" " + ", ".join(flat[j:j+18]) + ("," if j+18 < len(flat) else "") for j in range(0, len(flat), 18)] + out += ["};", END] + block = "\n".join(out) + + geo = open(args.geometry_c, encoding="utf-8").read() + i0, i1 = geo.find(BEGIN), geo.find(END) + if i0 < 0 or i1 < i0: + sys.exit("Error: markers not found in %s" % args.geometry_c) + new = geo[:i0] + block + geo[i1+len(END):] + if new == geo: + print("%s: logo data unchanged" % args.geometry_c) + elif args.dry_run: + print("%s: logo data would change (dry run, nothing written)" % args.geometry_c) + else: + open(args.geometry_c, "w", encoding="utf-8").write(new) + print("%s: logo data updated" % args.geometry_c) + + if args.preview: + import matplotlib; matplotlib.use("Agg") + import matplotlib.pyplot as plt + from matplotlib.collections import PolyCollection + fig, ax = plt.subplots(1, 2, figsize=(12, 6)) + ax[0].add_collection(PolyCollection([[V[a], V[b], V[c]] for a, b, c in tris], facecolor="#0099dc", edgecolor="none")) + ax[1].add_collection(PolyCollection([[V[a], V[b], V[c]] for a, b, c in tris], facecolor="#cfe9f7", edgecolor="#0099dc", lw=0.3)) + for a_ in ax: + a_.set_xlim(-0.55, 0.55); a_.set_ylim(-0.55, 0.55); a_.set_aspect("equal") + ax[0].set_title("filled triangles"); ax[1].set_title("triangulation") + plt.savefig(args.preview, dpi=100) + + +if __name__ == "__main__": + main() diff --git a/mcstas-comps/sources/ESS_butterfly.comp b/mcstas-comps/sources/ESS_butterfly.comp index 5f623b44fe..35535b4ee3 100644 --- a/mcstas-comps/sources/ESS_butterfly.comp +++ b/mcstas-comps/sources/ESS_butterfly.comp @@ -106,6 +106,7 @@ * tfocus_dist: [m] Position of time focusing window along z axis * tfocus_time: [s] Time position of time focusing window * tfocus_width: [s] Time width of time focusing window +* logo: [1] If 1, MCDISPLAY shows the ESS emblem in place of the target wheel; 0 suppresses it * * * @@ -117,7 +118,7 @@ DEFINE COMPONENT ESS_butterfly SETTING PARAMETERS (string sector="N",int beamline=1, yheight=0.03, cold_frac=0.5, int target_index=0, dist=0, focus_xw=0, focus_yh=0, c_performance=1, t_performance=1, Lmin, Lmax, tmax_multiplier=3, int n_pulses=1, - acc_power=5,tfocus_dist=0,tfocus_time=0,tfocus_width=0) + acc_power=5,tfocus_dist=0,tfocus_time=0,tfocus_width=0, int logo=1) SHARE %{ @@ -668,7 +669,7 @@ MCDISPLAY mcdis_pencolor ("#0099dc"); /* ESS blue, from the ess.eu logo */ magnify (""); butterfly_geometry (delta_y, jmax, cx, cz, orientation_angle, Beamlines, tx, ty, tz, rC1_x, rC1_z, rC2_x, rC2_z, rC3_x, rC3_z, rT1_x, rT1_z, rT2_x, rT2_z, - rT3_x, rT3_z, r11, r12, r21, r22, focus_xw, focus_yh); + rT3_x, rT3_z, r11, r12, r21, r22, focus_xw, focus_yh, logo); if (tbl_mode) { /* second cold wing emission plane, yellow as the others */ mcdis_pencolor ("#ffff00"); mcdis_dashed_line (rC4_x, -delta_y, rC4_z, rC5_x, -delta_y, rC5_z, 11); From f5b068ee67096520a3c7d2a215625a2728551fd1 Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Sat, 31 Oct 2026 13:03:13 +0100 Subject: [PATCH 5/6] =?UTF-8?q?ESS=5Fbutterfly:=20note=20that=20the=20ESS?= =?UTF-8?q?=20logo=20is=20=C2=A9=20ESS,=20all=20rights=20reserved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/sources/ESS_butterfly.comp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mcstas-comps/sources/ESS_butterfly.comp b/mcstas-comps/sources/ESS_butterfly.comp index 35535b4ee3..e9588bc1e5 100644 --- a/mcstas-comps/sources/ESS_butterfly.comp +++ b/mcstas-comps/sources/ESS_butterfly.comp @@ -78,6 +78,9 @@ * An ad-hoc method for future implementation of "engineering reality" is included, use the * "c_performance/t_performance" parameters to down-scale performance uniformly across all wavelengths. * +* ESS logo +* The ESS logo drawn in MCDISPLAY in place of the target wheel (logo=1) is © ESS. All rights reserved. +* * References: *
    *
  1. Release document "Update to ESS Moderators, latest version" From 6fac5b9602cd7323bc27df60c348b61a41cf9b5f Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Sat, 31 Oct 2026 13:07:33 +0100 Subject: [PATCH 6/6] ESS_butterfly: (c) ESS notice with the digitised logo data and script Co-Authored-By: Claude Opus 5.5 --- mcstas-comps/share/ESS_butterfly-geometry.c | 3 ++- mcstas-comps/share/ESS_butterfly-logo.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mcstas-comps/share/ESS_butterfly-geometry.c b/mcstas-comps/share/ESS_butterfly-geometry.c index d7dd5a9b28..5816f24305 100644 --- a/mcstas-comps/share/ESS_butterfly-geometry.c +++ b/mcstas-comps/share/ESS_butterfly-geometry.c @@ -28,7 +28,8 @@ double butterfly_e_x2[]= {3.3156e-05, 5.8985e-02, 1.1701e-01}; /* BEGIN generated ESS logo data (by ESS_butterfly-logo.py, do not edit by hand): ESS emblem from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg), 7 contours (holes running opposite to outlines) of 439 points in total, centred and scaled to unit - diameter with u to the right and v up, plus 437 cap triangles (indices into the points) */ + diameter with u to the right and v up, plus 437 cap triangles (indices into the points). + The ESS logo is (c) ESS. All rights reserved. */ int ess_logo_ncontours = 7; int ess_logo_contours[] = {0, 90, 90, 101, 191, 87, 278, 89, 367, 9, 376, 25, 401, 38}; int ess_logo_npoints = 439; diff --git a/mcstas-comps/share/ESS_butterfly-logo.py b/mcstas-comps/share/ESS_butterfly-logo.py index 3aa2f8f18a..33a7c80129 100755 --- a/mcstas-comps/share/ESS_butterfly-logo.py +++ b/mcstas-comps/share/ESS_butterfly-logo.py @@ -5,7 +5,7 @@ into polygon contours, triangulates the filled area (ear clipping, holes bridged in) and writes the result as C arrays into ESS_butterfly-geometry.c, between the "BEGIN/END generated ESS logo data" markers. Coordinates are centred on the emblem and scaled to unit diameter (largest extent 1), -u to the right and v upwards (SVG y flipped). +u to the right and v upwards (SVG y flipped). The ESS logo is (c) ESS. All rights reserved. Usage: python3 ESS_butterfly-logo.py logo-small.svg ESS_butterfly-geometry.c [--dry-run] [--tolerance 0.25] [--preview preview.png] @@ -225,8 +225,9 @@ def main(): " ESS emblem from the ess.eu logo (themes/custom/ess/graphics/logo-small.svg), %d contours" % len(rings), " (holes running opposite to outlines) of %d points in total, centred and scaled to unit" % len(V), - " diameter with u to the right and v up, plus %d cap triangles (indices into the points) */" + " diameter with u to the right and v up, plus %d cap triangles (indices into the points)." % len(tris), + " The ESS logo is (c) ESS. All rights reserved. */", "int ess_logo_ncontours = %d;" % len(rings), "int ess_logo_contours[] = {%s};" % ", ".join("%d, %d" % c for c in contours), "int ess_logo_npoints = %d;" % len(V)]