fig-laba3-obdelka.py 14 KB

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