fig-laba3-obdelka.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. # -*- coding: utf-8 -*-
  2. """
  3. Лаба 3, рисунок «Поперечное сечение обделки» (вариант 6).
  4. Геометрия по расчёту: l0 = 9550, f0 = 4244,4, r0 = 4808,2 мм (внутреннее
  5. очертание, центр под линией пяты); наружный свод r1 = 5836,8 из центра
  6. (0; f0 + hз - r1); пята hп = 980 по нормали, стена hст = 1120,
  7. обрез фундамента hо = 1400, обратный свод hл = 560, проезжая часть B = 8000.
  8. """
  9. import numpy as np
  10. import matplotlib
  11. matplotlib.use('Agg')
  12. import matplotlib.pyplot as plt
  13. # Чертёжный шрифт ГОСТ 2.304-81 тип А (прямой, из системных шрифтов Windows)
  14. import matplotlib.font_manager as fm
  15. fm.fontManager.addfont('C:/Windows/Fonts/GOST2304A.ttf')
  16. matplotlib.rcParams['font.family'] = 'GOST 2.304 type A'
  17. matplotlib.rcParams['mathtext.default'] = 'regular'
  18. matplotlib.rcParams['mathtext.fontset'] = 'custom'
  19. matplotlib.rcParams['mathtext.rm'] = 'GOST 2.304 type A'
  20. matplotlib.rcParams['mathtext.it'] = 'GOST 2.304 type A'
  21. matplotlib.rcParams['mathtext.bf'] = 'GOST 2.304 type A'
  22. L0, F0, R0 = 9550.0, 4244.4, 4808.2
  23. R1, HZ, HP, HST, HO, HL = 5836.8, 700.0, 1120.0 - 140.0, 1120.0, 1400.0, 560.0
  24. HP = 980.0
  25. G, HT = 8000.0, 80.0
  26. Y_OB = -600.0 # обрез фундамента
  27. Y_POD = Y_OB - HO # подошва выработки
  28. THR = np.arccos((R0 - F0) / R0) # угол пяты внутреннего контура (рад)
  29. C0 = np.array([0.0, F0 - R0])
  30. C1 = np.array([0.0, F0 + HZ - R1])
  31. U = np.array([np.sin(THR), np.cos(THR)]) # направление радиуса пяты
  32. P_IN = C0 + R0 * U
  33. P_OUT = C0 + (R0 + HP) * U
  34. X_WI, X_WO = P_IN[0], P_OUT[0] + HST * 0.0 + (HST - (P_OUT[0] - P_IN[0]))
  35. # наружная грань стены: от пяты (x = P_OUT[0], y = P_OUT[1]) вниз до обреза с
  36. # уширением до толщины HST по горизонтали у обреза
  37. X_WO_B = X_WI + HST
  38. F_IN, F_OUT = X_WI - 130.0, X_WO_B + 130.0 # грани фундамента
  39. EC = '#111111'
  40. def arc(c, r, t0, t1, n=140):
  41. t = np.linspace(np.radians(t0), np.radians(t1), n)
  42. return np.column_stack([c[0] + r * np.sin(t), c[1] + r * np.cos(t)])
  43. th_in = np.degrees(THR) # ~83.27
  44. th_out = np.degrees(np.arctan2(*(P_OUT - C1)[[1, 0]] * np.array([1, 1]))) if False else \
  45. np.degrees(np.arctan2(P_OUT[0] - C1[0], P_OUT[1] - C1[1]))
  46. inner_vault = arc(C0, R0, -th_in, th_in) # слева -> направо
  47. outer_vault = arc(C1, R1, -th_out, th_out)
  48. fig = plt.figure(figsize=(17.2 / 2.54, 9.6 / 2.54), dpi=250)
  49. matplotlib.rcParams['hatch.linewidth'] = 0.45
  50. ax = fig.add_axes([0, 0, 1, 1])
  51. ax.set_aspect('equal'); ax.axis('off')
  52. ax.set_xlim(-8600, 8900)
  53. ax.set_ylim(-2450, 5750)
  54. def poly(pts, hatch=None, fc='0.93', lw=1.7, z=2):
  55. p = np.asarray(pts)
  56. ax.fill(p[:, 0], p[:, 1], facecolor=fc, edgecolor=EC, lw=lw,
  57. hatch=hatch, zorder=z, closed=True)
  58. # --- А. кольцо свода со стенами ------------------------------------------------
  59. right_wall_out = np.array([P_OUT, [X_WO_B, Y_OB]])
  60. ring = np.vstack([
  61. outer_vault, # левая пята -> замок -> правая пята
  62. right_wall_out, # наружная грань правой стены
  63. [[X_WI, Y_OB]], # по обрезу внутрь
  64. [[P_IN[0], P_IN[1]]], # внутренняя грань правой стены вверх
  65. inner_vault[::-1], # внутренний свод справа -> налево
  66. [[-P_IN[0], P_IN[1]]], # левая стена вниз
  67. [[-X_WI, Y_OB]], # по обрезу наружу
  68. [[-X_WO_B, Y_OB], [-P_OUT[0], P_OUT[1]]] # наружная грань левой стены вверх
  69. ])
  70. poly(ring, hatch='////', z=2)
  71. # --- Б. фундаменты --------------------------------------------------------------
  72. poly([[F_IN, Y_OB], [F_OUT, Y_OB], [F_OUT, Y_POD], [F_IN, Y_POD]],
  73. hatch='\\\\\\\\', z=2)
  74. poly([[-F_IN, Y_OB], [-F_OUT, Y_OB], [-F_OUT, Y_POD], [-F_IN, Y_POD]],
  75. hatch='\\\\\\\\', z=2)
  76. # --- В. обратный свод ------------------------------------------------------------
  77. A_INV, F_INV = F_IN, 550.0 # полупролёт и стрела внутреннего свода
  78. R_INV = (A_INV ** 2 + F_INV ** 2) / (2 * F_INV)
  79. Y_INV_END = Y_OB - 100.0 # пята обратного свода на грани фундамента
  80. CY_INV = (Y_INV_END - F_INV) + R_INV # центр выше низа свода
  81. xi = np.linspace(-A_INV, A_INV, 120)
  82. inner_inv = np.column_stack([xi, CY_INV - np.sqrt(R_INV ** 2 - xi ** 2)])
  83. xo = np.linspace(-A_INV, A_INV, 120)
  84. outer_inv = np.column_stack([xo, inner_inv[:, 1][::-1] * 0 + (inner_inv[:, 1] - HL)[::-1]])
  85. outer_inv = np.column_stack([xo, inner_inv[:, 1] - HL])
  86. poly(np.vstack([inner_inv, [[A_INV, Y_INV_END]], outer_inv[::-1],
  87. [[-A_INV, Y_INV_END]]]), hatch='////', z=2)
  88. Y_BASE = Y_OB - 200.0 # низ основания проезжей части
  89. x_edge = np.sqrt(R_INV ** 2 - (CY_INV - Y_BASE) ** 2) # сопряжение с обратным сводом
  90. # щебёночная отсыпка между основанием проезжей части и обратным сводом:
  91. # линза с лёгкой заливкой и россыпью округлых камешков (как гравий на образце)
  92. m_lens = np.abs(xi) <= x_edge + 0.5
  93. lens = np.vstack([[-G / 2, Y_BASE], np.column_stack([xi[m_lens], inner_inv[m_lens, 1]]),
  94. [G / 2, Y_BASE]])
  95. poly(lens, fc='0.97', lw=0.9, z=2)
  96. rng = np.random.default_rng(6)
  97. def y_inv_at(x):
  98. return CY_INV - np.sqrt(R_INV ** 2 - x ** 2)
  99. for y_row, half, step, r_lo, r_hi in ((-875.0, 3400.0, 240.0, 40.0, 58.0),
  100. (-975.0, 2850.0, 260.0, 65.0, 105.0),
  101. (-1120.0, 1300.0, 240.0, 65.0, 100.0)):
  102. for xs in np.arange(-half, half + 1, step):
  103. xc = xs + rng.uniform(-60, 60)
  104. yc = y_row + rng.uniform(-25, 25)
  105. r0s = rng.uniform(r_lo, r_hi)
  106. hh = r0s * 0.72
  107. if yc - hh < y_inv_at(xc) + 12 or yc + hh > Y_BASE - 12:
  108. continue
  109. t = np.linspace(0, 2 * np.pi, 40)
  110. phi = rng.uniform(0, np.pi)
  111. rr = r0s * (1 + 0.05 * np.sin(2 * t + rng.uniform(0, 2 * np.pi)))
  112. px = xc + rr * np.cos(t) * np.cos(phi) - rr * 0.72 * np.sin(t) * np.sin(phi)
  113. py = yc + rr * np.cos(t) * np.sin(phi) + rr * 0.72 * np.sin(t) * np.cos(phi)
  114. ax.add_patch(plt.Polygon(np.column_stack([px, py]), closed=True,
  115. fc='white', ec=EC, lw=0.5, zorder=3))
  116. # --- Г. сточный треугольник и основание ------------------------------------------
  117. # проезжая часть доведена до внутренних граней фундаментов, низ повторяет свод
  118. y_in_full = CY_INV - np.sqrt(R_INV ** 2 - xi ** 2)
  119. bottom = np.maximum(y_in_full, Y_BASE)
  120. poly(np.vstack([[-F_IN, Y_OB], [F_IN, Y_OB],
  121. np.column_stack([xi[::-1], bottom[::-1]])]),
  122. hatch='....', fc='0.97', lw=1.1, z=2)
  123. poly([[-F_IN, Y_OB], [0, Y_OB + HT], [F_IN, Y_OB]], hatch='....', fc='0.97',
  124. lw=1.1, z=2)
  125. ax.plot([-F_IN, 0, F_IN], [Y_OB, Y_OB + HT, Y_OB], color=EC, lw=2.2, zorder=3)
  126. # --- ось тоннеля -------------------------------------------------------------------
  127. ax.plot([0, 0], [-2300, 5450], color='0.2', lw=0.7, ls=(0, (14, 4, 2, 4)), zorder=1)
  128. # --- размеры: засечки 45°, выносные с выступом, число у размерной линии ----------
  129. TICK = 120.0 # полудлина засечки
  130. EXT = 150.0 # выступ выносной линии за размерную
  131. GAP = 140.0 # зазор числа от размерной линии
  132. def tick(x, y, color=EC):
  133. ax.plot([x - TICK, x + TICK], [y - TICK, y + TICK], color=color, lw=1.5,
  134. solid_capstyle='butt', zorder=4)
  135. def dim_h(y, x1, x2, text, color=EC, above=True, fs=9.0):
  136. ax.plot([x1, x2], [y, y], color=color, lw=1.0, zorder=4)
  137. tick(x1, y, color); tick(x2, y, color)
  138. # белая подложка: ось тоннеля и выносные линии не проходят сквозь текст
  139. ax.text((x1 + x2) / 2, y + (GAP if above else -GAP), text, fontsize=fs,
  140. ha='center', va='bottom' if above else 'top', color=color,
  141. bbox=dict(facecolor='white', edgecolor='none', pad=1.2))
  142. def dim_v(x, y1, y2, text, color=EC, side='left', fs=9.0, gap=GAP):
  143. ax.plot([x, x], [y1, y2], color=color, lw=1.0, zorder=4)
  144. tick(x, y1, color); tick(x, y2, color)
  145. ax.text(x + (-gap if side == 'left' else gap), (y1 + y2) / 2, text, fontsize=fs,
  146. ha='center', va='center', color=color, rotation=90)
  147. def leader(xt, yt, xa, ya, text, ha='left', color=EC, fs=9.0):
  148. # выноска с полкой: единая ломаная от элемента к полке, текст над полкой
  149. shelf = 1050.0 if ha == 'left' else -1050.0
  150. ax.plot([xa, xt, xt + shelf], [ya, yt, yt], color=color, lw=0.7, zorder=3)
  151. ax.text(xt + (0.09 if ha == 'left' else -0.09) * shelf, yt + 90, text,
  152. fontsize=fs, ha=ha, va='bottom', color=color)
  153. # l0 — пролёт в свету: выносные от пят вниз с выступом
  154. dim_h(1050, -X_WI, X_WI, 'l$_0$ = 9550')
  155. ax.plot([X_WI, X_WI], [P_IN[1], 1050 - EXT], color='0.4', lw=0.5)
  156. ax.plot([-X_WI, -X_WI], [P_IN[1], 1050 - EXT], color='0.4', lw=0.5)
  157. # f0 — стрела подъёма в свету: выносные от пяты и замка влево с выступом
  158. dim_v(-2350, 0, F0, 'f$_0$ = 4244', side='right', gap=340)
  159. ax.plot([-X_WI, -2350 - EXT], [0, 0], color='0.4', lw=0.5)
  160. ax.plot([0, -2350 - EXT], [F0, F0], color='0.4', lw=0.5)
  161. # hз — замок: короткий размер, подпись над верхней засечкой; выносные от обоих
  162. # уровней замка (внутреннего и наружного очертаний)
  163. dim_v(-3450, F0, F0 + HZ, '')
  164. ax.text(-3450, F0 + HZ + 240, 'h$_з$ = 700', fontsize=9.0, ha='center', va='bottom')
  165. ax.plot([0, -3450 - EXT], [F0 + HZ, F0 + HZ], color='0.4', lw=0.5)
  166. ax.plot([0, -3450 - EXT], [F0, F0], color='0.4', lw=0.5)
  167. # B — проезжая часть: выносные от краёв вверх с выступом
  168. dim_h(-420, -G / 2, G / 2, 'B = 8000', above=True)
  169. ax.plot([-G / 2, -G / 2], [Y_OB, -420 + EXT], color='0.4', lw=0.5)
  170. ax.plot([G / 2, G / 2], [Y_OB, -420 + EXT], color='0.4', lw=0.5)
  171. # hт — сточный треугольник
  172. leader(3300, -120, 2400, Y_OB + HT * 0.55, 'h$_т$ = 80', ha='left')
  173. # r0, r1 — радиусы; тексты — вдоль своих радиусных линий
  174. rad0 = np.radians(38)
  175. u0 = np.array([np.sin(rad0), np.cos(rad0)])
  176. c0m = C0 + R0 * u0
  177. ax.plot([C0[0], c0m[0]], [C0[1], c0m[1]], color=EC, lw=0.8)
  178. ax.annotate('', xy=c0m, xytext=C0 + 780 * u0,
  179. arrowprops=dict(arrowstyle='-|>', color=EC, lw=0.9, mutation_scale=10))
  180. p_t0 = C0 + 3150 * u0 + 170 * np.array([-np.cos(rad0), np.sin(rad0)])
  181. ax.text(p_t0[0], p_t0[1], 'r$_0$ = 4808', fontsize=9.0, rotation=90 - 38,
  182. ha='center', va='center',
  183. bbox=dict(facecolor='white', edgecolor='none', pad=1.0))
  184. ax.plot(*C0, marker='+', ms=9, color=EC, mew=1.2)
  185. rad1 = np.radians(-63)
  186. u1 = np.array([np.sin(rad1), np.cos(rad1)])
  187. c1m = C1 + R1 * u1
  188. ax.plot([C1[0], c1m[0]], [C1[1], c1m[1]], color=EC, lw=0.8)
  189. ax.annotate('', xy=c1m, xytext=C1 + 900 * u1,
  190. arrowprops=dict(arrowstyle='-|>', color=EC, lw=0.9, mutation_scale=10))
  191. d1 = u1 # единичное направление радиуса r1
  192. x_t1 = -3900.0
  193. p_t1 = np.array([x_t1, C1[1] + (x_t1 - C1[0]) / d1[0] * d1[1]])
  194. p_t1 = p_t1 + 170 * np.array([-d1[1], -d1[0]])
  195. rot1 = np.degrees(np.arctan2(d1[1], d1[0])) - 180.0
  196. ax.text(p_t1[0], p_t1[1], 'r$_1$ = 5837', fontsize=9.0, rotation=rot1,
  197. ha='center', va='center',
  198. bbox=dict(facecolor='white', edgecolor='none', pad=1.0))
  199. ax.plot(*C1, marker='+', ms=9, color=EC, mew=1.2)
  200. # hп — пята по нормали (правая)
  201. n_off = np.array([np.cos(THR), -np.sin(THR)]) * 120
  202. a1 = C0 + (R0 + 60) * U + n_off
  203. a2 = C0 + (R0 + HP - 60) * U + n_off
  204. ax.plot([a1[0], a2[0]], [a1[1], a2[1]], color=EC, lw=0.8, zorder=4)
  205. tick(a1[0], a1[1]); tick(a2[0], a2[1])
  206. mid = (a1 + a2) / 2 + np.array([620, -80])
  207. ax.text(mid[0], mid[1], 'h$_п$ = 980', fontsize=9.0, ha='left', va='center',
  208. bbox=dict(facecolor='white', edgecolor='none', pad=1.0))
  209. # выносные: стена, фундамент, обратный свод
  210. # точка выноски h_ст — на наклонной наружной грани стены, выше засечек размера h_п
  211. t_w = 0.35
  212. w_pt = np.array([P_OUT[0] + t_w * (X_WO_B - P_OUT[0]), P_OUT[1] + t_w * (Y_OB - P_OUT[1])])
  213. leader(6800, 1100, w_pt[0], w_pt[1], 'h$_с$$_т$ = 1120', ha='left')
  214. # h_о — высота обреза фундамента: вертикальная размерная справа от фундамента
  215. xd_o = F_OUT + 300
  216. ax.plot([F_OUT, xd_o + EXT], [Y_OB, Y_OB], color='0.4', lw=0.5)
  217. ax.plot([F_OUT, xd_o + EXT], [Y_POD, Y_POD], color='0.4', lw=0.5)
  218. dim_v(xd_o, Y_POD, Y_OB, 'h$_о$ = 1400', side='right', gap=330)
  219. # h_л — толщина обратного свода: вертикальный размер, засечки на поверхностях свода
  220. x_l = 1500.0
  221. y_in = CY_INV - np.sqrt(R_INV ** 2 - x_l ** 2)
  222. dim_v(x_l, y_in - HL, y_in, '')
  223. ax.text(x_l + 180, y_in - HL - 60, 'h$_л$ = 560', fontsize=9.0, ha='left', va='top')
  224. fig.savefig('assets/images/laba3-obdelka.png', dpi=250, facecolor='white')
  225. print('сохранено: assets/images/laba3-obdelka.png')