civil-tools-v 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. CivilTools/Const/CAD.py +2 -0
  2. CivilTools/Const/Concrete.py +144 -0
  3. CivilTools/Const/Steel.py +21 -0
  4. CivilTools/Const/__init__.py +3 -0
  5. CivilTools/DXFGenerator/BasicDXF.py +238 -1
  6. CivilTools/DXFGenerator/DetailDXF.py +324 -0
  7. CivilTools/DXFGenerator/DrawingAttribs.py +45 -0
  8. CivilTools/DXFGenerator/LayerManager.py +37 -0
  9. CivilTools/DXFGenerator/__init__.py +3 -0
  10. CivilTools/FigureGenerator/BasicPNGPlotter.py +28 -25
  11. CivilTools/FigureGenerator/BasicPltPlotter.py +115 -1
  12. CivilTools/FigureGenerator/SeismicReport/ShearMassRatio.py +73 -0
  13. CivilTools/FigureGenerator/SeismicReport/ShearMoment.py +71 -0
  14. CivilTools/FigureGenerator/SeismicReport/__init__.py +2 -0
  15. CivilTools/FigureGenerator/StairCalculationSheetPNGPlotter.py +2 -8
  16. CivilTools/ReportGenerator/BasicGenerator.py +109 -83
  17. CivilTools/ReportGenerator/DocParagraph.py +3 -5
  18. CivilTools/ReportGenerator/DocPicture.py +7 -8
  19. CivilTools/ReportGenerator/DocTable.py +11 -11
  20. CivilTools/ReportGenerator/SeismicReport.py +302 -143
  21. CivilTools/ReportGenerator/SeismicReportTemplate.py +523 -202
  22. CivilTools/ReportGenerator/StairCalculationReport.py +249 -185
  23. CivilTools/ReportGenerator/UtilFunctions.py +108 -104
  24. CivilTools/ReportGenerator/__init__.py +2 -2
  25. CivilTools/YDBLoader/BuildingDefine/Beam/Beam.py +12 -15
  26. CivilTools/YDBLoader/BuildingDefine/Column/Column.py +5 -5
  27. CivilTools/YDBLoader/BuildingDefine/ComponentType.py +1 -1
  28. CivilTools/YDBLoader/BuildingDefine/Geometry/Grid.py +8 -12
  29. CivilTools/YDBLoader/BuildingDefine/Geometry/Joint.py +11 -10
  30. CivilTools/YDBLoader/BuildingDefine/Geometry/StandFloor.py +1 -1
  31. CivilTools/YDBLoader/BuildingDefine/GlobalResult/BasicResult.py +44 -24
  32. CivilTools/YDBLoader/BuildingDefine/GlobalResult/SeismicResult.py +168 -54
  33. CivilTools/YDBLoader/BuildingDefine/Section/Section.py +26 -31
  34. CivilTools/YDBLoader/BuildingDefine/Section/ShapeEnum.py +9 -9
  35. CivilTools/YDBLoader/BuildingDefine/Slab/Slab.py +1 -1
  36. CivilTools/YDBLoader/BuildingDefine/StairPart/LoadDefine.py +16 -10
  37. CivilTools/YDBLoader/BuildingDefine/StairPart/StairComponent.py +41 -37
  38. CivilTools/YDBLoader/BuildingDefine/StairPart/StairPart.py +133 -78
  39. CivilTools/YDBLoader/SQLiteConnector/Connector.py +16 -8
  40. CivilTools/YDBLoader/SQLiteConnector/RowDataFactory.py +19 -17
  41. CivilTools/YDBLoader/SQLiteConnector/YDBTableName.py +31 -20
  42. CivilTools/YDBLoader/YDBLoader.py +128 -110
  43. {civil_tools_v-0.0.1.dist-info → civil_tools_v-0.0.3.dist-info}/METADATA +88 -5
  44. civil_tools_v-0.0.3.dist-info/RECORD +60 -0
  45. {civil_tools_v-0.0.1.dist-info → civil_tools_v-0.0.3.dist-info}/WHEEL +1 -1
  46. civil_tools_v-0.0.1.dist-info/RECORD +0 -50
  47. {civil_tools_v-0.0.1.dist-info → civil_tools_v-0.0.3.dist-info}/LICENSE +0 -0
  48. {civil_tools_v-0.0.1.dist-info → civil_tools_v-0.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,324 @@
1
+ from typing import List
2
+ from .BasicDXF import BasicDXF
3
+ from .DrawingAttribs import (
4
+ DrawingAttribs,
5
+ PolylineAttribs,
6
+ TextAttribs,
7
+ TextEntityAlignment,
8
+ )
9
+ from .LayerManager import DefaultLayers, CADColor
10
+ from CivilTools.Const import Concrete, ConcreteLevel, Steel, SteelLevel
11
+
12
+
13
+ class DetailDXF(BasicDXF):
14
+ def __init__(self):
15
+ super().__init__()
16
+
17
+
18
+ default_floor_height_table_attribs = {
19
+ "column_con_level": None,
20
+ "column_steel_level": None,
21
+ "beam_con_level": None,
22
+ "beam_steel_level": None,
23
+ }
24
+
25
+
26
+ class FloorInformation:
27
+ def __init__(
28
+ self,
29
+ name: str,
30
+ archi_elevation: float,
31
+ plaster_thickness: float = 200,
32
+ beam_slab_con_mat: ConcreteLevel = Concrete.C35,
33
+ wall_con_mat: ConcreteLevel = Concrete.C60,
34
+ column_con_mat: ConcreteLevel = Concrete.C50,
35
+ brace_con_mat: ConcreteLevel = Concrete.C50,
36
+ beam_steel_mat: SteelLevel = Steel.HRB300,
37
+ column_steel_mat: SteelLevel = Steel.Q355,
38
+ brace_steel_mat: SteelLevel = Steel.Q355,
39
+ ):
40
+ if plaster_thickness < 0:
41
+ raise ValueError("Plaster value should be non-negitive.")
42
+ self.floor_name = name
43
+ self.__struct_elevation = archi_elevation - plaster_thickness
44
+ self.__archi_elevation = archi_elevation
45
+
46
+ self.beam_slab_con_mat = beam_slab_con_mat
47
+ self.wall_con_mat = wall_con_mat
48
+ self.column_con_mat = column_con_mat
49
+ self.brace_con_mat = brace_con_mat
50
+
51
+ self.beam_steel_mat = beam_steel_mat
52
+ self.column_steel_mat = column_steel_mat
53
+ self.brace_steel_mat = brace_steel_mat
54
+ self.is_embedded = False
55
+
56
+ def get_attribute_by_name(self, attrib_name: str):
57
+ if "楼层号" in attrib_name:
58
+ return self.floor_name
59
+ if "建筑" in attrib_name and "标高" in attrib_name:
60
+ return self.archi_elevation
61
+ if "结构" in attrib_name and "标高" in attrib_name:
62
+ return self.struct_elevation
63
+ if "柱" in attrib_name:
64
+ return self.column_con_mat.name
65
+ if "墙" in attrib_name:
66
+ return self.wall_con_mat.name
67
+ if "板" in attrib_name:
68
+ return self.beam_slab_con_mat.name
69
+
70
+ @property
71
+ def struct_elevation(self):
72
+ if self.__struct_elevation == 0:
73
+ return "±0.000"
74
+ return f"{self.__struct_elevation/1000:.3f}"
75
+
76
+ @property
77
+ def archi_elevation(self):
78
+ if self.__archi_elevation == 0:
79
+ return "±0.000"
80
+ return f"{self.__archi_elevation/1000:.3f}"
81
+
82
+
83
+ class FloorHeightTableDXF(BasicDXF):
84
+ def __prepare(self):
85
+ useful_layers = [DefaultLayers.TEXT, DefaultLayers.SYMBOL]
86
+ self.init_layers(useful_layers)
87
+
88
+ def __init__(
89
+ self,
90
+ above_floor_num: int,
91
+ under_floor_num: int,
92
+ base_elevation: float,
93
+ font_size: float = 300,
94
+ ):
95
+ super().__init__()
96
+ self.__prepare()
97
+ self.__above_floor_num = above_floor_num
98
+ self.__under_floor_num = under_floor_num
99
+ self.__base_elevation = base_elevation
100
+ self.floor_num = above_floor_num + under_floor_num
101
+ self.floors = self.__create_floors()
102
+ self.__font_size = font_size
103
+ self.__table_title = None
104
+ self.has_embeded_floor = False
105
+ self.embeded_floor = None
106
+ self.insert_point = [0, 0]
107
+
108
+ for key, value in default_floor_height_table_attribs.items():
109
+ setattr(self, key, value)
110
+
111
+ def __create_floors(self) -> List[FloorInformation]:
112
+ result = []
113
+ for i in range(self.__above_floor_num):
114
+ temp_floor = FloorInformation(f"F{i+1}", self.__base_elevation + 3500 * i)
115
+ result.append(temp_floor)
116
+ for i in range(self.__under_floor_num):
117
+ temp_floor = FloorInformation(
118
+ f"B{i+1}", self.__base_elevation - 3500 * (i + 1)
119
+ )
120
+ result.insert(0, temp_floor)
121
+ return result
122
+
123
+ def export_dxf(self, path):
124
+ self._remove_all_entities()
125
+ self.__draw_table_title()
126
+ self.__analysis_table_columns()
127
+ self.__draw_table_grid()
128
+ self.__draw_table_column_title()
129
+ self.__draw_context()
130
+ self.__draw_supplement_info()
131
+ self._save(path)
132
+
133
+ def set_table_title(self, title: str):
134
+ self.__table_title = title
135
+
136
+ def set_font_size(self, font_size: float):
137
+ self.__font_size = font_size
138
+
139
+ def set_embeding_floor(self, floor_name: str | None):
140
+ """Use None to reset embeded floor."""
141
+ if floor_name == None:
142
+ self.has_embeded_floor = False
143
+ self.embeded_floor = None
144
+ return
145
+ for floor in self.floors:
146
+ if floor.floor_name == floor_name:
147
+ floor.is_embedded = True
148
+ self.has_embeded_floor = True
149
+ self.embeded_floor = floor
150
+ return
151
+ raise ValueError(f"No floor named as {floor_name}")
152
+
153
+ def __analysis_table_columns(self):
154
+ self.__columns = []
155
+ self.__column_widths = []
156
+
157
+ self.__columns.append("当前层")
158
+ self.__column_widths.append(3)
159
+
160
+ self.__columns.append("建筑楼层号")
161
+ self.__column_widths.append(5)
162
+
163
+ self.__columns.append("建筑标高(m)")
164
+ self.__column_widths.append(6)
165
+
166
+ self.__columns.append("结构标高(m)")
167
+ self.__column_widths.append(6)
168
+
169
+ self.__first_part_column_num = len(self.__columns)
170
+
171
+ self.__columns.append("结构柱")
172
+ self.__column_widths.append(3)
173
+
174
+ self.__columns.append("剪力墙")
175
+ self.__column_widths.append(3)
176
+
177
+ self.__columns.append("梁/板")
178
+ self.__column_widths.append(3)
179
+
180
+ def __draw_table_title(self):
181
+ if self.__table_title == None:
182
+ return
183
+ text_attrib = TextAttribs(
184
+ DefaultLayers.TEXT.name,
185
+ text_height=self.__font_size,
186
+ text_align=TextEntityAlignment.MIDDLE_LEFT,
187
+ )
188
+ self._add_text(
189
+ self.__table_title,
190
+ [self.insert_point[0], self.insert_point[1] + self.__font_size * 1.5],
191
+ text_attrib,
192
+ )
193
+
194
+ def __draw_table_grid(self):
195
+ first_part_column_num = self.__first_part_column_num
196
+ start_x = self.insert_point[0]
197
+ start_y = self.insert_point[1]
198
+ font_factor = self.__font_size
199
+
200
+ total_width = font_factor * sum(self.__column_widths)
201
+ first_part_width = font_factor * sum(
202
+ self.__column_widths[:first_part_column_num]
203
+ )
204
+
205
+ total_height = font_factor * 2 * (self.floor_num + 2)
206
+ grid_draw_attrib = PolylineAttribs(DefaultLayers.SYMBOL.name)
207
+ for i in range(self.floor_num + 3):
208
+ if i == 1:
209
+ self._add_horizental_line(
210
+ start_x + first_part_width,
211
+ start_y,
212
+ total_width - first_part_width,
213
+ grid_draw_attrib,
214
+ )
215
+ else:
216
+ self._add_horizental_line(
217
+ start_x, start_y, total_width, grid_draw_attrib
218
+ )
219
+ start_y -= font_factor * 2
220
+ if (
221
+ i - 2 >= 0
222
+ and i - 2 < self.floor_num
223
+ and self.floors[::-1][i - 2].is_embedded
224
+ ):
225
+ self._add_horizental_line(
226
+ start_x, start_y + font_factor * 0.2, total_width, grid_draw_attrib
227
+ )
228
+ start_y = 0
229
+ for i in range(len(self.__columns) + 1):
230
+ if i <= first_part_column_num or i == len(self.__columns):
231
+ self._add_vertical_line(
232
+ start_x, start_y, -total_height, grid_draw_attrib
233
+ )
234
+ else:
235
+ self._add_vertical_line(
236
+ start_x,
237
+ start_y - font_factor * 2,
238
+ -total_height + font_factor * 2,
239
+ grid_draw_attrib,
240
+ )
241
+ if i == len(self.__columns):
242
+ continue
243
+ start_x += font_factor * self.__column_widths[i]
244
+
245
+ def __draw_table_column_title(self):
246
+ font_factor = self.__font_size
247
+ start_x = self.insert_point[0]
248
+ start_y = self.insert_point[1]
249
+
250
+ table_column_title_attrib = TextAttribs(
251
+ DefaultLayers.TEXT.name,
252
+ text_height=font_factor,
253
+ text_align=TextEntityAlignment.MIDDLE_CENTER,
254
+ )
255
+
256
+ for i in range(len(self.__columns)):
257
+ temp_string = self.__columns[i]
258
+ temp_x = (
259
+ sum(self.__column_widths[: i + 1]) - self.__column_widths[i] / 2
260
+ ) * font_factor
261
+ temp_y = (
262
+ -font_factor * 2
263
+ if i < self.__first_part_column_num
264
+ else -font_factor * 3
265
+ )
266
+ self._add_text(
267
+ temp_string,
268
+ [start_x + temp_x, start_y + temp_y],
269
+ table_column_title_attrib,
270
+ )
271
+ temp_x = (
272
+ (
273
+ sum(self.__column_widths)
274
+ + sum(self.__column_widths[: self.__first_part_column_num])
275
+ )
276
+ / 2.0
277
+ * font_factor
278
+ )
279
+ temp_y = -font_factor * 1
280
+ self._add_text("混凝土结构", [temp_x, temp_y], table_column_title_attrib)
281
+
282
+ def __draw_context(self):
283
+ font_factor = self.__font_size
284
+
285
+ start_x = self.insert_point[0]
286
+ start_y = self.insert_point[1] - font_factor * 4
287
+ for i in range(len(self.floors)):
288
+ temp_floor = self.floors[::-1][i]
289
+ for j in range(1, len(self.__columns)):
290
+ temp_insert_point = [
291
+ start_x + font_factor * (sum(self.__column_widths[: j + 1]) - 0.5),
292
+ start_y - font_factor * (2 * i + 1),
293
+ ]
294
+ temp_string = temp_floor.get_attribute_by_name(self.__columns[j])
295
+ self._add_text(
296
+ temp_string,
297
+ temp_insert_point,
298
+ TextAttribs(
299
+ DefaultLayers.TEXT.name,
300
+ color_index=CADColor.Yellow.value,
301
+ text_height=font_factor,
302
+ text_align=TextEntityAlignment.MIDDLE_RIGHT,
303
+ ),
304
+ )
305
+
306
+ def __draw_supplement_info(self):
307
+ if not self.has_embeded_floor:
308
+ return
309
+ font_factor = self.__font_size
310
+ temp_x = self.insert_point[0]
311
+ temp_y = self.insert_point[1] - font_factor * (self.floor_num + 2.5) * 2
312
+ table_supplement_info_attrib = TextAttribs(
313
+ DefaultLayers.TEXT.name,
314
+ text_height=font_factor,
315
+ text_align=TextEntityAlignment.MIDDLE_LEFT,
316
+ )
317
+ self._add_text(
318
+ f"上部嵌固层:{self.embeded_floor.floor_name},标高:{self.embeded_floor.struct_elevation}。",
319
+ [temp_x, temp_y],
320
+ table_supplement_info_attrib,
321
+ )
322
+
323
+ def __draw_embeding_dimension(self):
324
+ return NotImplemented
@@ -0,0 +1,45 @@
1
+ from ezdxf.enums import TextEntityAlignment
2
+ from CivilTools.Const import CADConst
3
+
4
+
5
+ class DrawingAttribs:
6
+ """这是一个基础的属性,包含图层、颜色等所有人都有的信息"""
7
+
8
+ def __init__(
9
+ self,
10
+ layer: str,
11
+ color_index: int = CADConst.BY_LAYER,
12
+ ):
13
+ self.layer = layer
14
+ self.color_index = color_index
15
+
16
+
17
+ class PolylineAttribs(DrawingAttribs):
18
+ """Polyline对象的属性,包括是否封闭等"""
19
+
20
+ def __init__(
21
+ self,
22
+ layer,
23
+ color_index: int = CADConst.BY_LAYER,
24
+ close: bool = False,
25
+ constant_width: float = 0,
26
+ ):
27
+ super().__init__(layer, color_index)
28
+ self.close = close
29
+ self.constant_width = constant_width
30
+ """全局宽度"""
31
+
32
+
33
+ class TextAttribs(DrawingAttribs):
34
+ def __init__(
35
+ self,
36
+ layer,
37
+ color_index: int = CADConst.BY_LAYER,
38
+ text_height: float = 300,
39
+ text_width_factor: float = 0.7,
40
+ text_align: TextEntityAlignment = TextEntityAlignment.MIDDLE_CENTER,
41
+ ):
42
+ super().__init__(layer, color_index)
43
+ self.text_height = text_height
44
+ self.text_width_factor = text_width_factor
45
+ self.text_align = text_align
@@ -0,0 +1,37 @@
1
+ from enum import Enum
2
+
3
+
4
+ class CADColor(Enum):
5
+ Red = 1
6
+ Yellow = 2
7
+ Green = 3
8
+ Cyan = 4
9
+ Blue = 5
10
+ Magenta = 6
11
+ White = 7
12
+ Gray = 8
13
+ LightGray = 9
14
+
15
+
16
+ class CADLineType(Enum):
17
+ Continuous = 1
18
+ DASHED = 2
19
+ CENTER = 3
20
+ DASHDOT = 4
21
+
22
+
23
+ class CADLayer:
24
+ def __init__(
25
+ self,
26
+ name: str,
27
+ color: CADColor,
28
+ line_type: CADLineType = CADLineType.Continuous,
29
+ ):
30
+ self.name = name
31
+ self.color = color
32
+ self.line_type = line_type
33
+
34
+
35
+ class DefaultLayers:
36
+ TEXT = CADLayer("0S-TEXT", CADColor.Cyan)
37
+ SYMBOL = CADLayer("0S-SYMB", CADColor.Red)
@@ -0,0 +1,3 @@
1
+ from .BasicDXF import BasicDXF, DrawingAttribs
2
+ from .LayerManager import CADColor,CADLayer,CADLineType
3
+ from .DetailDXF import FloorHeightTableDXF
@@ -1,43 +1,46 @@
1
1
  from PIL import Image, ImageDraw
2
2
  import warnings
3
3
 
4
+
4
5
  class BasicPNGPlotter:
5
- def __init__(self, width, height, bg = (0,0,0,0)):
6
+ def __init__(self, width, height, bg=(0, 0, 0, 0)):
6
7
  self.width = width
7
8
  self.height = height
8
- self.image = Image.new('RGBA', (width, height), bg)
9
+ self.image = Image.new("RGBA", (width, height), bg)
9
10
  self.draw = ImageDraw.Draw(self.image)
10
-
11
- def draw_horizental_line(self,s_x,s_y,length,color = "black",line_width = 2):
11
+
12
+ def draw_horizental_line(self, s_x, s_y, length, color="black", line_width=2):
12
13
  """画一段水平线,length为长度,正代表向右"""
13
- self.draw_line(s_x,s_y,s_x + length, s_y,color,line_width)
14
-
15
- def draw_vertical_line(self,s_x,s_y,length,color = "black",line_width = 2):
14
+ self.draw_line(s_x, s_y, s_x + length, s_y, color, line_width)
15
+
16
+ def draw_vertical_line(self, s_x, s_y, length, color="black", line_width=2):
16
17
  """画一段竖直线,length为长度,正代表向下"""
17
- self.draw_line(s_x,s_y,s_x + length, s_y,color,line_width)
18
-
19
- def draw_rectangle(self,s_x, s_y, rec_width, rec_height,color = "black",line_width = 2):
18
+ self.draw_line(s_x, s_y, s_x + length, s_y, color, line_width)
19
+
20
+ def draw_rectangle(
21
+ self, s_x, s_y, rec_width, rec_height, color="black", line_width=2
22
+ ):
20
23
  """画一个矩形,左上角点为s_x,s_y,width正代表向右,height正代表向下"""
21
- self.draw_horizental_line(s_x,s_y,rec_width,color,line_width)
22
- self.draw_horizental_line(s_x,s_y + rec_height,rec_width,color,line_width)
23
- self.draw_vertical_line(s_x,s_y,rec_height,color,line_width)
24
- self.draw_vertical_line(s_x + rec_width,s_y,rec_height,color,line_width)
25
-
26
- def draw_line(self,s_x,s_y,e_x,e_y,color = "black",width = 2):
27
- if s_x<0 or s_x > self.width or s_y <0 or s_y> self.height:
24
+ self.draw_horizental_line(s_x, s_y, rec_width, color, line_width)
25
+ self.draw_horizental_line(s_x, s_y + rec_height, rec_width, color, line_width)
26
+ self.draw_vertical_line(s_x, s_y, rec_height, color, line_width)
27
+ self.draw_vertical_line(s_x + rec_width, s_y, rec_height, color, line_width)
28
+
29
+ def draw_line(self, s_x, s_y, e_x, e_y, color="black", width=2):
30
+ if s_x < 0 or s_x > self.width or s_y < 0 or s_y > self.height:
28
31
  warnings.warn("Start point is out of figure.")
29
- if e_x<0 or e_x > self.width or e_y <0 or e_y> self.height:
32
+ if e_x < 0 or e_x > self.width or e_y < 0 or e_y > self.height:
30
33
  warnings.warn("End point is out of figure.")
31
- self.draw.line((s_x,s_y,e_x,e_y), fill=color, width=width)
32
-
34
+ self.draw.line((s_x, s_y, e_x, e_y), fill=color, width=width)
35
+
33
36
  def draw_png(self):
34
- self.draw.line((0, 0, 500, 500), fill='black', width=2)
37
+ self.draw.line((0, 0, 500, 500), fill="black", width=2)
35
38
 
36
- def save(self,path):
37
- self.image.save(path, 'PNG')
39
+ def save(self, path):
40
+ self.image.save(path, "PNG")
38
41
 
39
42
 
40
43
  if __name__ == "__main__":
41
- p = BasicPNGPlotter(1500,800)
44
+ p = BasicPNGPlotter(1500, 800)
42
45
  p.draw_png()
43
- p.save("testfiles/output.png")
46
+ p.save("testfiles/output.png")
@@ -1,3 +1,117 @@
1
+ from typing import List
2
+ import matplotlib.pyplot as plt
3
+ from matplotlib.axes import Axes
4
+ import io
5
+ import numpy as np
6
+
7
+ plt.rcParams["font.sans-serif"] = ["SimHei"] # 用来正常显示中文标签
8
+ plt.rcParams["axes.unicode_minus"] = False
9
+ plt.rcParams["font.size"] = 14
10
+
11
+
12
+ def GetTicks(n_max=1):
13
+ if n_max == 0:
14
+ n_max = 100
15
+ m = int(np.log10(n_max))
16
+ if m <= 0 and n_max <= 1:
17
+ m -= 1
18
+ p = n_max / (10**m)
19
+ if p <= 2.2:
20
+ result = [i * 1 * 10**m for i in range(3)]
21
+ elif p <= 3.5:
22
+ result = [i * 2 * 10**m for i in range(3)]
23
+ elif p <= 5:
24
+ result = [i * 2.5 * 10**m for i in range(3)]
25
+ elif p <= 6.9:
26
+ result = [i * 3 * 10**m for i in range(4)]
27
+ elif p <= 8:
28
+ result = [i * 2 * 10**m for i in range(5)]
29
+ else:
30
+ result = [i * 2 * 10**m for i in range(6)]
31
+ return result
32
+
1
33
 
2
34
  class BasicPltPlotter:
3
- pass
35
+ def __init__(self, fig_num: int = 1, fig_size=(10, 10)):
36
+ self.fig, _axes = plt.subplots(1, fig_num, figsize=fig_size)
37
+ if fig_num == 1:
38
+ self.axes = [_axes]
39
+ else:
40
+ self.axes = _axes
41
+ self.fig.patch.set_facecolor("none")
42
+ for ax in self.axes:
43
+ ax.patch.set_facecolor("none")
44
+ self.axes: List[Axes]
45
+ self.__remove_border()
46
+
47
+ def __remove_border(self):
48
+ for ax in self.axes:
49
+ ax.spines["right"].set_color("none")
50
+ ax.spines["top"].set_color("none")
51
+
52
+ def save_to_stream(self):
53
+ # 将图片保存到内存中的 BytesIO 对象
54
+ img_buffer = io.BytesIO()
55
+ self.fig.savefig(img_buffer, format="png", dpi=150) # 保存为 PNG 格式
56
+ plt.close() # 关闭图形,释放内存
57
+ # 将指针重置到流的开头,以便后续读取
58
+ img_buffer.seek(0)
59
+ return img_buffer
60
+
61
+
62
+ class SeismicPlotter(BasicPltPlotter):
63
+ def __init__(self, fig_num=2, floor_num: int = 8):
64
+ if fig_num != 1 and fig_num != 2:
65
+ raise ValueError("Only 1 or 2 is accepted for fig_num.")
66
+ if fig_num == 1:
67
+ fig_size = (3, 5)
68
+ else:
69
+ fig_size = (6, 5)
70
+ super().__init__(fig_num, fig_size)
71
+ self.kwargs_x = {
72
+ "label": "X",
73
+ "ls": "-",
74
+ "color": "k",
75
+ "marker": "o",
76
+ "ms": 3,
77
+ }
78
+ self.kwargs_y = {
79
+ "label": "X",
80
+ "ls": "-",
81
+ "color": "r",
82
+ "marker": "o",
83
+ "ms": 3,
84
+ }
85
+ self.floor_num = floor_num
86
+ self.y_label = "层号"
87
+ self._y_values = [i + 1 for i in range(self.floor_num)]
88
+ self._y_major_ticks = self.__create_y_ticks()
89
+ self._y_minor_ticks = [i + 1 for i in range(self.floor_num)]
90
+ self._ax1_x = [i for i in range(self.floor_num)]
91
+ self._ax1_y = [i * 0.5 for i in range(self.floor_num)]
92
+ self._ax2_x = [i for i in range(self.floor_num)]
93
+ self._ax2_y = [i * 0.5 for i in range(self.floor_num)]
94
+
95
+ def test_plot(self):
96
+ self.__plot()
97
+
98
+ def __plot(self):
99
+ self.axes[0].plot(self._ax1_x, self._y_values, **self.kwargs_x)
100
+ self.axes[0].plot(self._ax1_y, self._y_values, **self.kwargs_y)
101
+ self.axes[1].plot(self._ax2_x, self._y_values, **self.kwargs_x)
102
+ self.axes[1].plot(self._ax2_y, self._y_values, **self.kwargs_y)
103
+
104
+ def __create_y_ticks(self):
105
+ floor_num = self.floor_num
106
+ return range(0, int(floor_num) + 1, int(floor_num // 5) + 1)
107
+
108
+ def _validate_list_length(self, data_list, name):
109
+ """
110
+ 验证列表长度是否与楼层数相等
111
+ :param data_list: 待验证的列表
112
+ :param name: 列表的名称,用于异常信息
113
+ """
114
+ if len(data_list) != self.floor_num:
115
+ raise ValueError(
116
+ f"Length of {name} is not equal to floor number: {self.floor_num}!"
117
+ )
@@ -0,0 +1,73 @@
1
+ from ..BasicPltPlotter import SeismicPlotter, GetTicks
2
+ from typing import List
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+
6
+
7
+ class ShearMassRatioPlotter(SeismicPlotter):
8
+ def __init__(self, fig_num=2, floor_num=8):
9
+ super().__init__(fig_num, floor_num)
10
+ self.__limit = None
11
+ self.type = "剪重比"
12
+
13
+ def set_data(self, shear_x: List[float], shear_y: List[float], mass: List[float]):
14
+ if len(shear_x) != self.floor_num:
15
+ raise ValueError(
16
+ f"Lenght of shear_x is not equal to floor number: {self.floor_num}!"
17
+ )
18
+ if len(shear_y) != self.floor_num:
19
+ raise ValueError(
20
+ f"Lenght of shear_y is not equal to floor number: {self.floor_num}!"
21
+ )
22
+ if len(mass) != self.floor_num:
23
+ raise ValueError(
24
+ f"Lenght of mass is not equal to floor number: {self.floor_num}!"
25
+ )
26
+
27
+ self._ax1_x = np.array(shear_x) / np.array(mass)
28
+ self._ax2_x = np.array(shear_y) / np.array(mass)
29
+
30
+ def set_limit(self, limit: float):
31
+ self.__limit = limit
32
+
33
+ def plot(self):
34
+ if self.__limit:
35
+ self.__plot_limit()
36
+ kwargs_x = self.kwargs_x.copy()
37
+ kwargs_x["label"] = "X"
38
+ kwargs_y = self.kwargs_x.copy()
39
+ kwargs_y["label"] = "Y"
40
+ self.axes[0].plot(self._ax1_x, self._y_values, **kwargs_x)
41
+ self.axes[1].plot(self._ax2_x, self._y_values, **kwargs_y)
42
+ self.__adjust_lim()
43
+ self.__add_titles()
44
+
45
+ def __plot_limit(self):
46
+ limitation = self.__limit
47
+ for ax in self.axes:
48
+ ax.vlines(
49
+ x=limitation,
50
+ ymin=0,
51
+ ymax=self.floor_num,
52
+ color="r",
53
+ linewidth=3,
54
+ ls="--",
55
+ label=f"限值{limitation*100:.1f}%",
56
+ )
57
+
58
+ def __adjust_lim(self):
59
+ xmaxs = [self._ax1_x.max(), self._ax2_x.max()]
60
+ for i in range(2):
61
+ self.axes[i].set_xlim(left=0, right=xmaxs[i] * 1.2)
62
+ self.axes[i].set_yticks(self._y_major_ticks)
63
+ self.axes[i].set_yticks(self._y_minor_ticks, minor=True)
64
+ x_ticks = GetTicks(xmaxs[i])
65
+ self.axes[i].set_xticks(x_ticks)
66
+ self.axes[i].set_xticklabels([f"{i*100:.1f}%" for i in x_ticks])
67
+
68
+ def __add_titles(self):
69
+ self.axes[0].set_ylabel(self.y_label)
70
+ self.axes[0].set_xlabel(f"X小震下{self.type}")
71
+ self.axes[1].set_xlabel(f"Y小震下{self.type}")
72
+ self.axes[0].legend(framealpha=0, fontsize=12, loc=4)
73
+ self.axes[1].legend(framealpha=0, fontsize=12, loc=4)