ararpy 0.0.24__py3-none-any.whl → 0.1.11__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.
ararpy/smp/export.py CHANGED
@@ -17,6 +17,7 @@ import sys
17
17
  import pickle
18
18
  import traceback
19
19
  import pdf_maker as pm
20
+ from decimal import Decimal
20
21
 
21
22
  from ..calc import arr, isochron
22
23
  from . import basic, sample, consts
@@ -39,6 +40,7 @@ def to_pdf(file_path: str, figure: str, smp: Sample):
39
40
  pdf = CreatePDF(filepath=file_path, sample=smp)
40
41
  pdf.save(figure=figure)
41
42
 
43
+
42
44
  class ExcelTemplate:
43
45
  def __init__(self, **kwargs):
44
46
  self.name = ""
@@ -937,314 +939,30 @@ class CreateOriginGraph:
937
939
 
938
940
  class CreatePDF:
939
941
  def __init__(self, **kwargs):
940
- self.name = "PDF"
941
- self.sample = Sample()
942
- self.figure = Plot()
943
942
  self.filepath = ""
944
- self.page_size = [595, 842]
945
- self.data_bytes = b""
946
- self.component = []
947
- self.text = []
948
- self.frame = []
949
- self.axis_area = [138, 400, 360, 270] # x0, y0, w, h
950
- # with open(os.path.join(SETTINGS_ROOT, 'PDF_Template.txt'), 'rb') as f:
951
- # self.data_str: str = f.read().decode('utf-8')
943
+ self.sample = Sample()
944
+ # self.name = "PDF"
945
+ # self.figure = Plot()
946
+ # self.page_size = [595, 842]
947
+ # self.data_bytes = b""
948
+ # self.component = []
949
+ # self.text = []
950
+ # self.frame = []
951
+ # self.axis_area = [138, 400, 360, 270] # x0, y0, w, h
952
+
952
953
  for key, value in kwargs.items():
953
954
  setattr(self, key, value)
954
955
 
955
- def _xmin(self):
956
- return float(self.figure.xaxis.min)
957
-
958
- def _xmax(self):
959
- return float(self.figure.xaxis.max)
960
-
961
- def _ymin(self):
962
- return float(self.figure.yaxis.min)
963
-
964
- def _ymax(self):
965
- return float(self.figure.yaxis.max)
966
-
967
- def _get_transfer_pos(self, x, y):
968
- x0, y0, w, h = self.axis_area
969
- x = (x - self._xmin()) / (self._xmax() - self._xmin()) * w + x0
970
- y = (y - self._ymin()) / (self._ymax() - self._ymin()) * h + y0
971
- return [x, y]
972
-
973
- def _get_isochron_line(self, point1, point2, color='1 0 0', width=1):
974
- line_str = ''
975
- if not len(point1) == len(point2) == 2:
976
- return line_str
977
- x0, y0, w, h = self.axis_area
978
-
979
- def _get_line_points(k, m):
980
- if k == 0:
981
- return [
982
- [x0, m], [x0 + w, m]
983
- ]
984
- return [
985
- [x0, x0 * k + m], [x0 + w, (x0 + w) * k + m],
986
- [(y0 - m) / k, y0], [(y0 + h - m) / k, y0 + h]
987
- ]
988
-
989
- point_1 = self._get_transfer_pos(*point1)
990
- point_2 = self._get_transfer_pos(*point2)
991
- k = (point_2[1] - point_1[1]) / (point_2[0] - point_1[0])
992
- m = point_2[1] - point_2[0] * k
993
- line = []
994
- for point in _get_line_points(k, m):
995
- if self.is_in_area(*point):
996
- line.append(point)
997
- if len(line) == 2:
998
- line_str = f'{width} w\r{color} RG\r{line[0][0]} {line[0][1]} m {line[1][0]} {line[1][1]} l S\r'
999
- return line_str
1000
-
1001
- def _get_spectra_line(self, data, width=1, color='1 0 0'):
1002
- """
1003
- data = [[x1, x2, ..., xn], [y1, y2, ..., yn]]
1004
- """
1005
- x0, y0, w, h = self.axis_area
1006
- num = 0
1007
- line_str = ''
1008
- if not data:
1009
- return line_str
1010
- data = arr.transpose(data)
1011
- for index, point in enumerate(data):
1012
- point = self._get_transfer_pos(*point)
1013
- if not self.is_in_area(*point):
1014
- if index == 0 and self.is_in_area(*self._get_transfer_pos(*data[index + 1])):
1015
- point[0] = x0
1016
- elif index == (len(data) - 1) and self.is_in_area(*self._get_transfer_pos(*data[index - 1])):
1017
- point[0] = x0 + w
1018
- elif 0 < index < (len(data) - 1) and (
1019
- self.is_in_area(*self._get_transfer_pos(*data[index + 1])) or self.is_in_area(
1020
- *self._get_transfer_pos(*data[index - 1]))):
1021
- if x0 < point[0] < (x0 + w):
1022
- point[1] = [y0, y0 + h][point[1] >= y0 + h]
1023
- else:
1024
- point[0] = [x0, x0 + w][point[0] >= x0 + w]
1025
- else:
1026
- continue
1027
- line_str = line_str + f'{point[0]} {point[1]} {"m " if num == 0 else "l "}'
1028
- num += 1
1029
- line_str = f'{width} w\r{color} RG\r' + line_str + 'S\r'
1030
- return line_str
1031
-
1032
- def is_in_area(self, x, y):
1033
- x0, y0, w, h = self.axis_area
1034
- if x == -999:
1035
- x = x0
1036
- if y == -999:
1037
- y = y0
1038
- return x0 <= x <= x0 + w and y0 <= y <= y0 + h
1039
-
1040
- def set_axis_frame(self):
1041
- from decimal import Decimal
1042
- frame = ''
1043
- x0, y0, w, h = self.axis_area
1044
- frame += f'1 w\r0 0 0 RG\r{x0} {y0} {w} {h} re S\r' # % 四个参数:最小x,最小y,宽度和高度
1045
-
1046
- xmin, xmax = float(self.figure.xaxis.min), float(self.figure.xaxis.max)
1047
- nx, dx = int(self.figure.xaxis.split_number), float(self.figure.xaxis.interval)
1048
- ymin, ymax = float(self.figure.yaxis.min), float(self.figure.yaxis.max)
1049
- ny, dy = int(self.figure.yaxis.split_number), float(self.figure.yaxis.interval)
1050
-
1051
- for i in range(ny + 1):
1052
- yi = y0 + i * h * dy / (ymax - ymin)
1053
- if self.is_in_area(-999, yi):
1054
- frame += f'{x0} {yi} m {x0 - 4} {yi} l S\r'
1055
- frame += f'BT\r1 0 0 1 {x0 - 4 - 32} {yi - 4} Tm\r/F1 12 Tf\r0 0 0 rg\r({Decimal(str(ymin)) + i * Decimal(str(dy))}) Tj\rET\r'
1056
- for i in range(nx + 1):
1057
- xi = x0 + i * w * dx / (xmax - xmin)
1058
- if self.is_in_area(xi, -999):
1059
- frame += f'{xi} {y0} m {xi} {y0 - 4} l S\r'
1060
- frame += f'BT\r1 0 0 1 {xi - 12} {y0 - 16} Tm\r/F1 12 Tf\r0 0 0 rg\r({Decimal(str(xmin)) + i * Decimal(str(dx))}) Tj\rET\r'
1061
- self.frame.append(frame)
1062
- return frame
1063
-
1064
- def set_main_content(self):
1065
- content = ''
1066
- if self.figure.type == 'isochron':
1067
- scatter_w, scatter_h = 5, 5
1068
- if not arr.is_empty(self.figure.line1.info):
1069
- content += self._get_isochron_line(*self.figure.line1.data, width=1, color='1 0 0')
1070
- if not arr.is_empty(self.figure.line2.info):
1071
- content += self._get_isochron_line(*self.figure.line2.data, width=1, color='0 0 1')
1072
- for point in arr.transpose(self.figure.data):
1073
- x, y = self._get_transfer_pos(point[0], point[2])
1074
- if self.is_in_area(x, y):
1075
- if int(point[5]) - 1 in self.sample.SelectedSequence1:
1076
- color = '0 0 0 RG\r1 0 0 rg\r'
1077
- elif int(point[5]) - 1 in self.sample.SelectedSequence2:
1078
- color = '0 0 0 RG\r0 0 1 rg\r'
1079
- else:
1080
- color = '0 0 0 RG\r1 1 1 rg\r'
1081
- content = content + color + \
1082
- f'{x - scatter_w / 2} {y - scatter_h / 2} {scatter_w} {scatter_h} re b\r'
1083
- elif self.figure.type == 'spectra':
1084
- for index, data in enumerate([self.figure.data, self.figure.set1.data, self.figure.set2.data]):
1085
- color = ['0 0 0', '1 0 0', '0 0 1'][index]
1086
- data = arr.transpose(data)
1087
- content = content + self._get_spectra_line(data[:2], color=color) + \
1088
- self._get_spectra_line([data[0], data[2]], color=color)
1089
- self.component.append(content)
1090
- return content
1091
-
1092
- def set_text(self):
1093
- text = ''
1094
- x0, y0, w, h = self.axis_area
1095
- # Figure Title
1096
- text += f'BT\r1 0 0 1 {x0 + 10} {y0 - 20 + h} Tm\n/F1 12 Tf\r({self.sample.Info.sample.name}) Tj\rET\r'
1097
- if self.figure.type == 'isochron':
1098
- xaxis_title_number = ''.join(list(filter(str.isdigit, self.figure.xaxis.title.text)))
1099
- yaxis_title_number = ''.join(list(filter(str.isdigit, self.figure.yaxis.title.text)))
1100
- # X axis title
1101
- x_title_length = 5 * 12 # length * font point size
1102
- text += '\n'.join([
1103
- 'BT', f'1 0 0 1 {x0 + w / 2 - x_title_length / 2} {y0 - 30} Tm',
1104
- # % 使用Tm将文本位置设置为(35,530)前四个参数是cosx, sinx, -sinx, cosx表示逆时针旋转弧度
1105
- '/F1 8 Tf', '5 Ts', f'({xaxis_title_number[:2]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar / ) Tj',
1106
- '/F1 8 Tf', '5 Ts', f'({xaxis_title_number[2:4]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar) Tj', 'ET',
1107
- ])
1108
- # Y axis title
1109
- y_title_length = 5 * 12 # length * font point size
1110
- text += '\n'.join([
1111
- 'BT', f'0 1 -1 0 {x0 - 40} {y0 + h / 2 - y_title_length / 2} Tm',
1112
- # % 使用Tm将文本位置设置为(35,530)前四个参数是cosx, sinx, -sinx, cosx表示逆时针旋转弧度
1113
- '/F1 8 Tf', '5 Ts', f'({yaxis_title_number[:2]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar / ) Tj',
1114
- '/F1 8 Tf', '5 Ts', f'({yaxis_title_number[2:4]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar) Tj', 'ET',
1115
- ])
1116
-
1117
- elif self.figure.type == 'spectra':
1118
- # X axis title
1119
- x_title_length = 13 * 12 # length * font point size
1120
- text += '\n'.join([
1121
- 'BT', f'1 0 0 1 {x0 + w / 2 - x_title_length / 2} {y0 - 30} Tm',
1122
- '/F1 12 Tf', '0 Ts', '(Cumulative ) Tj', '/F1 8 Tf', '5 Ts', f'(39) Tj',
1123
- '/F1 12 Tf', '0 Ts', '(Ar Released (%)) Tj', 'ET',
1124
- ])
1125
- # Y axis title
1126
- y_title_length = 9 * 12 # length * font point size
1127
- text += '\n'.join([
1128
- 'BT', f'0 1 -1 0 {x0 - 40} {y0 + h / 2 - y_title_length / 2} Tm',
1129
- '/F1 12 Tf', '0 Ts', f'(Apparent Age (Ma)) Tj', 'ET',
1130
- ])
1131
- # Text 1
1132
- info = self.figure.set1.info
1133
- if len(info) == 8 and self.figure.text1.text != '':
1134
- sum39 = findall('∑{sup|39}Ar = (.*)', self.figure.text1.text)[1]
1135
- text += '\n'.join([
1136
- 'BT', f'1 0 0 1 {x0 + w / 4} {y0 + h / 2} Tm',
1137
- '/F1 12 Tf', '0 Ts', f'(t) Tj', '/F1 8 Tf', '-2 Ts', f'(p) Tj',
1138
- '/F1 12 Tf', '0 Ts',
1139
- f'( = {info[4]:.2f} <261> {info[6]:.2f} Ma, MSMD = {info[3]:.2f}, ∑) Tj',
1140
- '/F1 8 Tf', '5 Ts', f'(39) Tj',
1141
- '/F1 12 Tf', '0 Ts',
1142
- f'(Ar = {sum39}) Tj',
1143
- 'ET',
1144
- ])
1145
- # Text 2
1146
- text2 = findall('∑{sup|39}Ar = (.*)', self.figure.text2.text)[1]
1147
-
1148
- self.text.append(text)
1149
- return text
1150
-
1151
- def set_split_line(self):
1152
- others = []
1153
- for i in range(200):
1154
- if i * 50 >= self.page_size[0]:
1155
- break
1156
- others.append(f'[2] 0 d\n{i * 50} 0 m {i * 50} {self.page_size[1]} l S')
1157
- for i in range(200):
1158
- if i * 50 >= self.page_size[1]:
1159
- break
1160
- others.append(f'[2] 0 d\n0 {i * 50} m {self.page_size[0]} {i * 50} l S')
1161
- self.data_str = self.data_str.replace(
1162
- '% <flag: others>\r',
1163
- '% <flag: others>\r' + '0.75 G\n' + '\n'.join(others),
1164
- )
1165
-
1166
- def set_info(self):
1167
- from datetime import datetime, timezone, timedelta
1168
- date = str(datetime.now(tz=timezone(offset=timedelta(hours=8))))
1169
- date = findall('(.*)-(.*)-(.*) (.*):(.*):(.*)\.(.*)', date)[0]
1170
- date = ''.join(date[0:6])
1171
- date = 'D:' + date + "+08'00'"
1172
- self.data_str = self.data_str.replace(
1173
- '% <flag: info CreationDate>',
1174
- f"{date}",
1175
- )
1176
- self.data_str = self.data_str.replace(
1177
- '% <flag: info ModDate>',
1178
- f"{date}",
1179
- )
1180
-
1181
- self.data_str = self.data_str.replace(
1182
- '% <flag: info Title>',
1183
- f'{self.sample.Info.sample.name} - {self.figure.name}'
1184
- )
1185
- self.data_str = self.data_str.replace(
1186
- '% <flag: page title>\r',
1187
- '% <flag: page title>\r' +
1188
- f'(<This is a demo of the exported PDF.>) Tj T*\n'
1189
- f'(<The PDFs can be freely edited in Adobe Illustrator.>) Tj\n'
1190
- )
1191
-
1192
- def set_replace(self):
1193
- self.data_str = self.data_str.replace(
1194
- '% <main contents>\r',
1195
- '% <main contents>\r' + '\r\n'.join(self.component)
1196
- )
1197
- self.data_str = self.data_str.replace(
1198
- '% <frames>\r',
1199
- '% <frames>\r' + '\r\n'.join(self.frame)
1200
- )
1201
- self.data_str = self.data_str.replace(
1202
- '% <texts>\r',
1203
- '% <texts>\r' + '\r\n'.join(self.text)
1204
- )
1205
-
1206
- def get_pdf(self):
1207
- self.do_function(
1208
- self.set_main_content,
1209
- self.set_axis_frame,
1210
- self.set_text,
1211
- self.set_info,
1212
- self.set_replace,
1213
- # self.set_split_line,
1214
- self.toBetys,
1215
- self.save,
1216
- )
1217
-
1218
- def get_contents(self):
1219
- self.do_function(
1220
- self.set_main_content,
1221
- self.set_axis_frame,
1222
- self.set_text,
1223
- )
1224
- return {
1225
- 'component': self.component,
1226
- 'frame': self.frame,
1227
- 'text': self.text,
1228
- }
1229
-
1230
- def toBetys(self):
1231
- self.data_bytes = self.data_str.encode('utf-8')
1232
- return self.data_bytes
1233
-
1234
- # def save(self):
1235
- # with open(self.filepath, 'wb') as f:
1236
- # f.write(self.data_bytes)
956
+ def color_hex_to_rgb(self, color: str):
957
+ if color.startswith("#"):
958
+ color = color[1:]
959
+ r = int(color[:2], 16)
960
+ g = int(color[2:4], 16)
961
+ b = int(color[4:6], 16)
962
+ return r, g, b
1237
963
 
1238
- def do_function(self, *handlers):
1239
- for handler in handlers:
1240
- try:
1241
- handler()
1242
- except Exception:
1243
- print(traceback.format_exc())
1244
- continue
1245
-
1246
- def create_pdf(self):
1247
- pass
964
+ def color_rgb_normalized(self, rgb):
965
+ return [round(i / 255, 6) for i in rgb]
1248
966
 
1249
967
  def save(self, figure: str = "figure_3", use_split_number: bool = True):
1250
968
 
@@ -1262,9 +980,10 @@ class CreatePDF:
1262
980
  file = pm.NewPDF(filepath=self.filepath)
1263
981
  # rich text tags should follow this priority: color > script > break
1264
982
  file.text(page=0, x=50, y=780, line_space=1.2, size=12, base=0, h_align="left",
1265
- text=f"The PDF producer is still under improvement.<r>"
1266
- f"The PDF can be edited with Adobe Acrobat and Illustrator.<r>"
1267
- f"<r><sup>40</sup>Ar/<sup>39</sup>Ar Plot")
983
+ text=f"The PDF can be edited with Adobe Acrobat, Illustrator and CorelDRAW.<r>"
984
+ f"<r> Sample Name: {self.sample.name()}"
985
+ f"<r> Figure Title: {basic.get_component_byid(self.sample, figure).name}",
986
+ )
1268
987
  file.canvas(page=1, margin_top=5, canvas=cv, unit="cm", h_align="middle")
1269
988
 
1270
989
  # save pdf
@@ -1318,9 +1037,26 @@ class CreatePDF:
1318
1037
 
1319
1038
  # isochron scatters
1320
1039
  data = arr.transpose(plot.data)
1040
+ colors = [
1041
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set1.color)),
1042
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set2.color)),
1043
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set3.color)),
1044
+ ]
1045
+ stroke_colors = [
1046
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set1.border_color)),
1047
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set2.border_color)),
1048
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.set3.border_color))
1049
+ ]
1321
1050
  for (x, sx, y, sy, r, i) in data:
1322
- pt.scatter(x, y, fill_color="red" if (i - 1) in set1.data else "blue" if (i - 1) in set2.data else "white",
1323
- size=2)
1051
+ if (i - 1) in set1.data:
1052
+ pt.scatter(x, y, stroke_color=stroke_colors[0], fill_color=colors[0], line_width=int(float(plot.set1.border_width) / 2),
1053
+ size=int(float(plot.set1.symbol_size) / 3), z_index=10)
1054
+ elif (i - 1) in set2.data:
1055
+ pt.scatter(x, y, stroke_color=stroke_colors[1], fill_color=colors[1], line_width=int(float(plot.set2.border_width) / 2),
1056
+ size=int(float(plot.set2.symbol_size) / 3), z_index=10)
1057
+ else:
1058
+ pt.scatter(x, y, stroke_color=stroke_colors[2], fill_color=colors[2], line_width=int(float(plot.set3.border_width) / 2),
1059
+ size=int(float(plot.set3.symbol_size) / 3), z_index=10)
1324
1060
 
1325
1061
  # split sticks
1326
1062
  # xaxis.interval = (xaxis_max - xaxis_min) / xaxis.split_number
@@ -1330,45 +1066,61 @@ class CreatePDF:
1330
1066
  end = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1331
1067
  end = (end[0], start[1] - 5)
1332
1068
  if not pt.is_out_side(*start):
1333
- pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt")
1334
- pt.text(x=start[0], y=end[1] - 15, text=f"{xaxis_min + xaxis.interval * i}", clip=False,
1335
- coordinate="pt", h_align="middle")
1069
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=100)
1070
+ pt.text(x=start[0], y=end[1] - 15, clip=False, coordinate="pt", h_align="middle",
1071
+ text=f"{Decimal(str(xaxis_min)) + Decimal(str(xaxis.interval)) * Decimal(str(i))}", z_index=150)
1336
1072
  for i in range(yaxis.split_number + 1):
1337
1073
  start = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1338
1074
  end = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1339
1075
  end = (start[0] - 5, end[1])
1340
1076
  if not pt.is_out_side(*start):
1341
- pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt")
1342
- pt.text(x=end[0] - 5, y=end[1], text=f"{yaxis_min + yaxis.interval * i}", clip=False,
1343
- coordinate="pt", h_align="right", v_align="center")
1344
- # pt.text(x=end[0] - 5, y=end[1], text=arr.change_number_format([f"{yaxis_min + yaxis.interval * i}"], flag="Scientific", precision=1)[0], clip=False,
1345
- # coordinate="pt", h_align="right", v_align="center")
1346
-
1077
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=100)
1078
+ pt.text(x=end[0] - 5, y=end[1], clip=False, coordinate="pt", h_align="right", v_align="center",
1079
+ text=f"{Decimal(str(yaxis_min)) + Decimal(str(yaxis.interval)) * Decimal(str(i))}", z_index=150)
1347
1080
 
1348
1081
  # axis titles
1349
-
1350
1082
  p = pt.scale_to_points((xaxis_max + xaxis_min) / 2, yaxis_min)
1351
- pt.text(x=p[0], y=p[1] - 30, text=x_title, clip=False, coordinate="pt", h_align="middle", v_align="top")
1083
+ pt.text(x=p[0], y=p[1] - 30, text=x_title, clip=False, coordinate="pt",
1084
+ h_align="middle", v_align="top", z_index=150)
1352
1085
  p = pt.scale_to_points(xaxis_min, (yaxis_max + yaxis_min) / 2)
1353
1086
  pt.text(x=p[0] - 50, y=p[1], text=y_title, clip=False, coordinate="pt",
1354
- h_align="middle", v_align="bottom", rotate=90)
1087
+ h_align="middle", v_align="bottom", rotate=90, z_index=150)
1355
1088
 
1356
1089
  # inside text
1090
+ colors = [self.color_rgb_normalized(self.color_hex_to_rgb(plot.line1.color)),
1091
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.line2.color))]
1092
+ widths = [int(float(plot.line1.line_width) / 2), int(float(plot.line2.line_width) / 2)]
1093
+ styles = [plot.line1.line_type, plot.line2.line_type]
1357
1094
  for index, data in enumerate([plot.line1.data, plot.line2.data]):
1358
1095
  # isochron line
1359
1096
  try:
1360
- pt.line(start=data[0], end=data[1], clip=True, width=1, color=colors[index])
1097
+ pt.line(start=data[0], end=data[1], clip=True, color=colors[index],
1098
+ width=widths[index], line_style=styles[index], line_caps='square', z_index=50)
1361
1099
  except IndexError:
1362
1100
  pass
1363
1101
  if data != []:
1102
+ if index == 0:
1103
+ pos = [round(i / 100, 2) for i in plot.text1.pos]
1104
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.text1.color))
1105
+ elif index == 1:
1106
+ pos = [round(i / 100, 2) for i in plot.text2.pos]
1107
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.text2.color))
1108
+ else:
1109
+ pos = (0.6, 0.7)
1110
+ color = "black"
1364
1111
  age, sage = round(age_results[index]['age'], 2), round(age_results[index]['s2'], 2)
1365
1112
  F, sF = round(age_results[index]['F'], 2), round(age_results[index]['sF'], 2)
1366
1113
  R0, sR0 = round(age_results[index]['initial'], 2), round(age_results[index]['sinitial'], 2)
1367
- pt.text(x=(xaxis_max - xaxis_min) * 0.6 + xaxis_min,
1368
- y=(yaxis_max - yaxis_min) * 0.7 + yaxis_min,
1114
+ MSWD, R2 = round(age_results[index]['MSWD'], 2), round(age_results[index]['R2'], 4)
1115
+ Chisq, p = round(age_results[index]['Chisq'], 2), round(age_results[index]['Pvalue'], 2)
1116
+ pt.text(x=(xaxis_max - xaxis_min) * pos[0] + xaxis_min,
1117
+ y=(yaxis_max - yaxis_min) * pos[1] + yaxis_min,
1369
1118
  text=f"Age ={age} {chr(0xb1)} {sage} Ma<r>F = {F} {chr(0xb1)} {sF}<r>"
1370
- f"R<sub>0</sub> = {R0} {chr(0xb1)} {sR0}",
1371
- clip=True, coordinate="scale", h_align="middle", v_align="center", rotate=0)
1119
+ f"R<sub>0</sub> = {R0} {chr(0xb1)} {sR0}<r>"
1120
+ f"{MSWD = }, R<sup>2</sup> = {R2}<r>"
1121
+ f"{Chisq = }, {p = }",
1122
+ size=10, clip=True, coordinate="scale", h_align="left", v_align="bottom",
1123
+ color=color, rotate=0, z_index=150)
1372
1124
  return cv
1373
1125
 
1374
1126
  def plot_spectra(self, smp: Sample = None, figure: str = "figure_1"):
@@ -1396,7 +1148,6 @@ class CreatePDF:
1396
1148
  yaxis_max = float(yaxis.max)
1397
1149
 
1398
1150
  plot_scale = (xaxis_min, xaxis_max, yaxis_min, yaxis_max)
1399
- colors = ['red', 'color']
1400
1151
 
1401
1152
  # create a canvas
1402
1153
  cv = pm.Canvas(width=17, height=12, unit="cm", show_frame=True, clip_outside_plot_areas=False)
@@ -1406,32 +1157,62 @@ class CreatePDF:
1406
1157
 
1407
1158
  # spectra lines
1408
1159
  data = plot.data
1160
+ colors = [
1161
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.line1.color)),
1162
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.line2.color)),
1163
+ ]
1164
+ widths = [int(float(plot.line1.line_width) / 2), int(float(plot.line2.line_width) / 2)]
1165
+ styles = [plot.line1.line_type, plot.line2.line_type]
1409
1166
  for index in range(len(data) - 1):
1410
1167
  pt.line(start=(data[index][0], data[index][1]), end=(data[index + 1][0], data[index + 1][1]),
1411
- clip=True, width=1, color='black')
1168
+ width=widths[0], line_style=styles[0], color=colors[0],
1169
+ clip=True, line_caps="square", z_index=9)
1412
1170
  pt.line(start=(data[index][0], data[index][2]), end=(data[index + 1][0], data[index + 1][2]),
1413
- clip=True, width=1, color='black')
1414
-
1171
+ width=widths[1], line_style=styles[1], color=colors[1],
1172
+ clip=True, line_caps="square", z_index=9)
1173
+
1174
+ colors = [
1175
+ [self.color_rgb_normalized(self.color_hex_to_rgb(plot.line3.color)),
1176
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.line4.color))],
1177
+ [self.color_rgb_normalized(self.color_hex_to_rgb(plot.line5.color)),
1178
+ self.color_rgb_normalized(self.color_hex_to_rgb(plot.line6.color))]
1179
+ ]
1180
+ widths = [
1181
+ [int(float(plot.line3.line_width) / 2), int(float(plot.line4.line_width) / 2)],
1182
+ [int(float(plot.line5.line_width) / 2), int(float(plot.line6.line_width) / 2)]
1183
+ ]
1184
+ styles = [[plot.line3.line_type, plot.line4.line_type], [plot.line5.line_type, plot.line6.line_type]]
1415
1185
  for index, data in enumerate([plot.set1.data, plot.set2.data]):
1416
1186
  if not data:
1417
1187
  continue
1418
- color = colors[index]
1419
1188
  for i in range(len(data) - 1):
1420
1189
  pt.line(start=(data[i][0], data[i][1]), end=(data[i + 1][0], data[i + 1][1]),
1421
- clip=True, width=1, color=color)
1190
+ width=widths[index][0], line_style=styles[index][0], color=colors[index][0],
1191
+ clip=True, line_caps="square", z_index=99)
1422
1192
  pt.line(start=(data[i][0], data[i][2]), end=(data[i + 1][0], data[i + 1][2]),
1423
- clip=True, width=1, color=color)
1193
+ width=widths[index][1], line_style=styles[index][1], color=colors[index][1],
1194
+ clip=True, line_caps="square", z_index=99)
1195
+ if index == 0:
1196
+ pos = [round(i / 100, 2) for i in plot.text1.pos]
1197
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.text1.color))
1198
+ elif index == 1:
1199
+ pos = [round(i / 100, 2) for i in plot.text2.pos]
1200
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.text2.color))
1201
+ else:
1202
+ pos = (0.6, 0.7)
1203
+ color = "black"
1424
1204
  age, sage = round(age_results[index]['age'], 2), round(age_results[index]['s2'], 2)
1425
1205
  F, sF = round(age_results[index]['F'], 2), round(age_results[index]['sF'], 2)
1426
1206
  Num = int(age_results[index]['Num'])
1427
1207
  MSWD, Ar39 = round(age_results[index]['MSWD'], 2), round(age_results[index]['Ar39'], 2)
1428
1208
  Chisq, Pvalue = round(age_results[index]['Chisq'], 2), round(age_results[index]['Pvalue'], 2)
1429
- pt.text(x=(xaxis_max - xaxis_min) * 0.6 + xaxis_min,
1430
- y=(yaxis_max - yaxis_min) * 0.7 + yaxis_min,
1209
+ pt.text(x=(xaxis_max - xaxis_min) * pos[0] + xaxis_min,
1210
+ y=(yaxis_max - yaxis_min) * pos[1] + yaxis_min,
1431
1211
  text=f"Age ={age} {chr(0xb1)} {sage} Ma<r>WMF = {F} {chr(0xb1)} {sF}, n = {Num}<r>"
1432
1212
  f"MSWD = {MSWD}, <sup>39</sup>Ar = {Ar39}%<r>"
1433
1213
  f"Chisq = {Chisq}, p = {Pvalue}",
1434
- clip=True, coordinate="scale", h_align="middle", v_align="center", rotate=0)
1214
+ size=10, clip=True, coordinate="scale", h_align="middle", v_align="center",
1215
+ color=color, rotate=0, z_index=150)
1435
1216
 
1436
1217
  # split sticks
1437
1218
  for i in range(xaxis.split_number + 1):
@@ -1439,8 +1220,87 @@ class CreatePDF:
1439
1220
  end = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1440
1221
  end = (end[0], start[1] - 5)
1441
1222
  if not pt.is_out_side(*start):
1442
- pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt")
1223
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=100)
1443
1224
  pt.text(x=start[0], y=end[1] - 15, text=f"{xaxis_min + xaxis.interval * i}", clip=False,
1225
+ coordinate="pt", h_align="middle", z_index=150)
1226
+ for i in range(yaxis.split_number + 1):
1227
+ start = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1228
+ end = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1229
+ end = (start[0] - 5, end[1])
1230
+ if not pt.is_out_side(*start):
1231
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=100)
1232
+ pt.text(x=end[0] - 5, y=end[1], text=f"{yaxis_min + yaxis.interval * i}", clip=False,
1233
+ coordinate="pt", h_align="right", v_align="center", z_index=150)
1234
+
1235
+ # axis titles
1236
+ p = pt.scale_to_points((xaxis_max + xaxis_min) / 2, yaxis_min)
1237
+ pt.text(x=p[0], y=p[1] - 30, text=x_title, clip=False, coordinate="pt",
1238
+ h_align="middle", v_align="top", z_index=150)
1239
+ p = pt.scale_to_points(xaxis_min, (yaxis_max + yaxis_min) / 2)
1240
+ pt.text(x=p[0] - 50, y=p[1], text=y_title, clip=False, coordinate="pt",
1241
+ h_align="middle", v_align="bottom", rotate=90, z_index=150)
1242
+
1243
+ return cv
1244
+
1245
+ def plot_degas(self, smp: Sample = None, figure: str = 'figure_8'): # create a canvas
1246
+ if smp is None:
1247
+ smp = self.sample
1248
+ if figure != "figure_8":
1249
+ raise ValueError
1250
+
1251
+ x_title = f""
1252
+ y_title = f""
1253
+
1254
+ plot: Plot = smp.DegasPatternPlot
1255
+ title = plot.title.text
1256
+ xaxis: Plot.Axis = plot.xaxis
1257
+ yaxis: Plot.Axis = plot.yaxis
1258
+ x_title = xaxis.title.text
1259
+ y_title = yaxis.title.text
1260
+ xaxis_min = float(xaxis.min)
1261
+ xaxis_max = float(xaxis.max) + 2
1262
+ yaxis_min = float(yaxis.min)
1263
+ yaxis_max = float(yaxis.max)
1264
+ plot_scale = (xaxis_min, xaxis_max, yaxis_min, yaxis_max)
1265
+
1266
+ colors = ['red', 'color']
1267
+
1268
+ # data
1269
+ data = plot.data # 36a, 37ca, 38cl, 39k, 40r, 36, 37, 38, 39, 40
1270
+
1271
+ # create a canvas
1272
+ cv = pm.Canvas(width=17, height=12, unit="cm", show_frame=True, clip_outside_plot_areas=False)
1273
+ # change frame outline style
1274
+ cv.show_frame(color="grey", line_width=0.5)
1275
+ pt = cv.add_plot_area(name="Plot1", plot_area=(0.15, 0.15, 0.8, 0.8), plot_scale=plot_scale, show_frame=True)
1276
+
1277
+ COLOR_MAP_1: list = [
1278
+ # 3399FF, 33FF99, CCCC00, FF6666,
1279
+ [0.200, 0.600, 1.000], [0.200, 1.000, 0.600], [0.800, 0.800, 0.000], [1.000, 0.400, 0.400],
1280
+ # 00FFFF, 00994C, FF8000, 7F00FF
1281
+ [0.000, 1.000, 1.000], [0.000, 0.600, 0.298], [1.000, 0.502, 0.000], [0.498, 0.000, 1.000],
1282
+ # 3333FF, FF3399
1283
+ [1.000, 0.200, 0.600], [0.200, 0.200, 1.000],
1284
+ ]
1285
+
1286
+ # spectra lines
1287
+ data = plot.data
1288
+ for i, isotope in enumerate(data):
1289
+ color = COLOR_MAP_1[i]
1290
+ for index, point in enumerate(isotope):
1291
+ # plot scatter pints
1292
+ pt.scatter(index + 1, point, fill_color="white", stroke_color=color, size=2)
1293
+ if index != 0:
1294
+ pt.line(start=(index, isotope[index - 1]), end=(index + 1, point), clip=True, width=1, color=color)
1295
+
1296
+ # split sticks
1297
+ for i in range(xaxis.split_number + 1):
1298
+ start = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1299
+ end = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1300
+ end = (end[0], start[1] - 5)
1301
+ if not pt.is_out_side(*start):
1302
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt")
1303
+ pt.text(x=start[0], y=end[1] - 15, text=f"{int(xaxis_min + xaxis.interval * i)}", clip=False,
1444
1304
  coordinate="pt", h_align="middle")
1445
1305
  for i in range(yaxis.split_number + 1):
1446
1306
  start = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
@@ -1460,19 +1320,103 @@ class CreatePDF:
1460
1320
 
1461
1321
  return cv
1462
1322
 
1463
- def plot_degas(self, figure: str = 'figure_8'): # create a canvas
1464
- cv = pm.Canvas(width=17, height=12, unit="cm", show_frame=True, clip_outside_plot_areas=False)
1465
- # change frame outline style
1466
- cv.show_frame(color="grey", line_width=0.5)
1467
- return cv
1323
+ def plot_age_distribution(self, smp: Sample = None, figure: str = 'figure_9'):
1324
+ if smp is None:
1325
+ smp = self.sample
1326
+ if figure != "figure_9":
1327
+ raise ValueError
1328
+
1329
+ plot: Plot = smp.AgeDistributionPlot
1330
+ title = plot.title.text
1331
+ xaxis: Plot.Axis = plot.xaxis
1332
+ yaxis: Plot.Axis = plot.yaxis
1333
+ x_title = f"<sup>40</sup>Ar/<sup>39</sup>Ar Age (Ma)"
1334
+ y_title = yaxis.title.text
1335
+ xaxis_min = float(xaxis.min)
1336
+ xaxis_max = float(xaxis.max)
1337
+ yaxis_min = float(yaxis.min)
1338
+ yaxis_max = float(yaxis.max)
1339
+ plot_scale = (xaxis_min, xaxis_max, yaxis_min, yaxis_max)
1468
1340
 
1469
- def plot_age_distribution(self, figure: str = 'figure_9'): # create a canvas
1470
1341
  cv = pm.Canvas(width=17, height=12, unit="cm", show_frame=True, clip_outside_plot_areas=False)
1471
1342
  # change frame outline style
1472
1343
  cv.show_frame(color="grey", line_width=0.5)
1473
- return cv
1344
+ pt = cv.add_plot_area(name="Plot1", plot_area=(0.15, 0.15, 0.8, 0.8), plot_scale=plot_scale, show_frame=True)
1345
+
1346
+ # histogram
1347
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.set1.border_color))
1348
+ fill_color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.set1.color))
1349
+ for i in range(plot.set1.bin_count):
1350
+ pt.rect(left_bottom=[plot.set1.data[2][i][0], 0], width=plot.set1.bin_width, height=plot.set1.data[1][i],
1351
+ line_width=0, color=fill_color, fill_color=fill_color, fill=True, coordinate='scale', z_index=0)
1352
+ pt.line(start=[plot.set1.data[2][i][0], 0], end=[plot.set1.data[2][i][0], plot.set1.data[1][i]],
1353
+ color=color, line_style=plot.set1.border_type, width=plot.set1.border_width,
1354
+ coordinate='scale', z_index=1)
1355
+ pt.line(start=[plot.set1.data[2][i][0], plot.set1.data[1][i]],
1356
+ end=[plot.set1.data[2][i][0] + plot.set1.bin_width, plot.set1.data[1][i]],
1357
+ color=color, line_style=plot.set1.border_type, width=plot.set1.border_width,
1358
+ coordinate='scale', z_index=1)
1359
+ pt.line(start=[plot.set1.data[2][i][0] + plot.set1.bin_width, plot.set1.data[1][i]],
1360
+ end=[plot.set1.data[2][i][0] + plot.set1.bin_width, 0],
1361
+ color=color, line_style=plot.set1.border_type, width=plot.set1.border_width,
1362
+ coordinate='scale', z_index=1)
1363
+
1364
+ # KDE
1365
+ scale = max(plot.set1.data[1]) / max(plot.set2.data[1])
1366
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.set2.color))
1367
+ for i in range(plot.set2.band_points):
1368
+ pt.line(start=[plot.set2.data[0][i], plot.set2.data[1][i] * scale],
1369
+ end=[plot.set2.data[0][i + 1], plot.set2.data[1][i + 1] * scale],
1370
+ color=color, line_style=plot.set2.line_type, width=plot.set2.line_width,
1371
+ coordinate='scale', z_index=5)
1372
+
1373
+ # age bars
1374
+ ages = sorted(zip(*plot.set3.data), key=lambda x: x[0])
1375
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.set3.color))
1376
+ fill_color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.set3.border_color))
1377
+ ppu_y = pt.ppu("y")
1378
+ height = float(plot.set3.bar_height) / ppu_y
1379
+ interval = float(plot.set3.bar_interval) / ppu_y if plot.set3.vertical_align == "bottom" else ((yaxis_max - yaxis_min) - height * len(ages)) / (len(ages) + 1)
1380
+ for index, age in enumerate(ages):
1381
+ pt.rect(left_bottom=[age[0] - age[1], interval + index * (interval + height)],
1382
+ width=age[1] * 2, height=height, color=color, fill_color=fill_color, fill=True,
1383
+ line_width=plot.set3.border_width, coordinate='scale', z_index=1)
1384
+
1385
+ pos = [round(i / 100, 2) for i in plot.text1.pos]
1386
+ color = self.color_rgb_normalized(self.color_hex_to_rgb(plot.text1.color))
1387
+ text = plot.text1.text.replace('\n', '<r>')
1388
+ pt.text(x=(xaxis_max - xaxis_min) * pos[0] + xaxis_min,
1389
+ y=(yaxis_max - yaxis_min) * pos[1] + yaxis_min,
1390
+ text=text, size=10, clip=True, coordinate="scale", color=color,
1391
+ h_align="middle", v_align="center", rotate=0, z_index=100)
1392
+
1393
+ # split sticks
1394
+ for i in range(xaxis.split_number + 1):
1395
+ start = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1396
+ end = pt.scale_to_points(xaxis_min + xaxis.interval * i, yaxis_min)
1397
+ end = (end[0], start[1] - 5)
1398
+ if not pt.is_out_side(*start):
1399
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=50)
1400
+ pt.text(x=start[0], y=end[1] - 15, text=f"{int(xaxis_min + xaxis.interval * i)}", clip=False,
1401
+ coordinate="pt", h_align="middle", z_index=100)
1402
+ for i in range(yaxis.split_number + 1):
1403
+ start = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1404
+ end = pt.scale_to_points(xaxis_min, yaxis_min + yaxis.interval * i)
1405
+ end = (start[0] - 5, end[1])
1406
+ if not pt.is_out_side(*start):
1407
+ pt.line(start=start, end=end, width=1, line_style="solid", clip=False, coordinate="pt", z_index=50)
1408
+ pt.text(x=end[0] - 5, y=end[1], text=f"{yaxis_min + yaxis.interval * i}", clip=False,
1409
+ coordinate="pt", h_align="right", v_align="center", z_index=100)
1474
1410
 
1411
+ # axis titles
1412
+ p = pt.scale_to_points((xaxis_max + xaxis_min) / 2, yaxis_min)
1413
+ pt.text(x=p[0], y=p[1] - 30, text=x_title, clip=False, coordinate="pt",
1414
+ h_align="middle", v_align="top", z_index=150)
1415
+ p = pt.scale_to_points(xaxis_min, (yaxis_max + yaxis_min) / 2)
1416
+ pt.text(x=p[0] - 50, y=p[1], text=y_title, clip=False, coordinate="pt",
1417
+ h_align="middle", v_align="bottom", rotate=90, z_index=150)
1475
1418
 
1419
+ return cv
1476
1420
 
1477
1421
 
1478
1422
  class CustomUnpickler(pickle.Unpickler):