Coverage for JLC2KiCadLib/footprint/model3d.py: 88%

1 import logging

2 import os

3 import re

4

5 import requests

6 from KicadModTree import Model

7

8 wrl_header = """#VRML V2.0 utf8

9#created by JLC2KiCad_lib using the JLCPCB library

10#for more info see https://github.com/TousstNicolas/JLC2KICAD_lib

11"""

12

13

14 def mil2mm(data):

15 return float(data) / 3.937

16

17

18 def get_StepModel(

19 component_uuid,

20 footprint_info,

21 kicad_mod,

22 translationX,

23 translationY,

24 translationZ,

25 rotation,

26):

27 logging.info("Downloading STEP Model ...")

28

29 # `qAxj6KHrDKw4blvCG8QJPs7Y` is a constant in

30 # https://modules.lceda.cn/smt-gl-engine/0.8.22.6032922c/smt-gl-engine.js

31 # and points to the bucket containing the step files.

32

33 response = requests.get(

34 f"https://modules.easyeda.com/qAxj6KHrDKw4blvCG8QJPs7Y/{component_uuid}"

35 )

36

37 if response.status_code != requests.codes.ok:

38 logging.error("request error, no Step model found")

39 return

40

41 ensure_footprint_lib_directories_exist(footprint_info)

42 filename = (

43 f"{footprint_info.output_dir}/"

44 f"{footprint_info.footprint_lib}/"

45 f"{footprint_info.model_dir}/"

46 f"{footprint_info.footprint_name}.step"

47 )

48 with open(filename, "wb") as f:

49 f.write(response.content)

50

51 logging.info(f"STEP model created at {filename}")

52

53 if footprint_info.model_base_variable:

54 if footprint_info.model_base_variable.startswith("$"):

55 path_name = (

56 f'"{footprint_info.model_base_variable}/'

57 f"{footprint_info.model_dir}/"

58 f'{footprint_info.footprint_name}.step"'

59 )

60 else:

61 path_name = (

62 f'"$({footprint_info.model_base_variable})/'

63 f"{footprint_info.model_dir}/"

64 f'{footprint_info.footprint_name}.step"'

65 )

66 else:

67 path_name = f"{footprint_info.model_dir}/{footprint_info.footprint_name}.step"

68

69 translationX = (translationX - footprint_info.origin[0]) / 100

70 translationY = -(translationY - footprint_info.origin[1]) / 100

71 translationZ = float(translationZ) / 100

72

73 kicad_mod.append(

74 Model(

75 filename=path_name,

76 at=[translationX, translationY, translationZ],

77 rotate=[-float(axis_rotation) for axis_rotation in rotation.split(",")],

78 )

79 )

80 logging.info(f"added {path_name} to footprint")

81

82

83 def get_WrlModel(

84 component_uuid,

85 footprint_info,

86 kicad_mod,

87 translationX,

88 translationY,

89 translationZ,

90 rotation,

91):

92 logging.info("Creating WRL model ...")

93

94 response = requests.get(

95 f"https://easyeda.com/analyzer/api/3dmodel/{component_uuid}"

96 )

97 if response.status_code == requests.codes.ok:

98 text = response.content.decode()

99 else:

100 logging.error("request error, no 3D model found")

101 return ()

102

103 wrl_content = wrl_header

104

105 # get material list

106 pattern = "newmtl .*?endmtl"

107 matchs = re.findall(pattern=pattern, string=text, flags=re.DOTALL)

108

109 materials = {}

110 for match in matchs:

111 material = {}

112 material_id = ""

113 for value in match.split("\n"):

114 if value[0:6] == "newmtl":

115 material_id = value.split(" ")[1]

116 elif value[0:2] == "Ka":

117 material["ambientColor"] = value.split(" ")[1:]

118 elif value[0:2] == "Kd":

119 material["diffuseColor"] = value.split(" ")[1:]

120 elif value[0:2] == "Ks":

121 material["specularColor"] = value.split(" ")[1:]

122 elif value[0] == "d":

123 material["transparency"] = value.split(" ")[1]

124

125 materials[material_id] = material

126

127 # get vertices list

128 pattern = "v (.*?)\n"

129 matchs = re.findall(pattern=pattern, string=text, flags=re.DOTALL)

130

131 vertices = []

132 for vertice in matchs:

133 vertices.append(

134 " ".join(

135 [str(round(float(coord) / 2.54, 4)) for coord in vertice.split(" ")]

136 )

137 )

138

139 # get shape list

140 shapes = text.split("usemtl")[1:]

141 for shape in shapes:

142 lines = shape.split("\n")

143 material = materials[lines[0].replace(" ", "")]

144 index_counter = 0

145 link_dict = {}

146 coordIndex = []

147 points = []

148 for line in lines[1:]:

149 if len(line) > 0:

150 face = [int(index) for index in line.replace("//", "").split(" ")[1:]]

151 face_index = []

152 for index in face:

153 if index not in link_dict:

154 link_dict[index] = index_counter

155 face_index.append(str(index_counter))

156 points.append(vertices[index - 1])

157 index_counter += 1

158 else:

159 face_index.append(str(link_dict[index]))

160 face_index.append("-1")

161 coordIndex.append(",".join(face_index) + ",")

162 points.insert(-1, points[-1])

163

164 shape_str = f"""

165 Shape{{

166 appearance Appearance {{

167 material Material {{

168 diffuseColor {" ".join(material["diffuseColor"])}

169 specularColor {" ".join(material["specularColor"])}

170 ambientIntensity 0.2

171 transparency {material["transparency"]}

172 shininess 0.5

173 }}

174 }}

175 geometry IndexedFaceSet {{

176 ccw TRUE

177 solid FALSE

178 coord DEF co Coordinate {{

179 point [

180 {(", ").join(points)}

181 ]

182 }}

183 coordIndex [

184 {"".join(coordIndex)}

185 ]

186 }}

187}}"""

188

189 wrl_content += shape_str

190

191 ensure_footprint_lib_directories_exist(footprint_info)

192

193 filename = (

194 f"{footprint_info.output_dir}/"

195 f"{footprint_info.footprint_lib}/"

196 f"{footprint_info.model_dir}/"

197 f"{footprint_info.footprint_name}.wrl"

198 )

199 with open(filename, "w") as f:

200 f.write(wrl_content)

201

202 if footprint_info.model_base_variable:

203 if footprint_info.model_base_variable.startswith("$"):

204 path_name = (

205 f'"{footprint_info.model_base_variable}/'

206 f"{footprint_info.model_dir}/"

207 f'{footprint_info.footprint_name}.wrl"'

208 )

209 else:

210 path_name = (

211 f'"$({footprint_info.model_base_variable})/'

212 f"{footprint_info.model_dir}/"

213 f'{footprint_info.footprint_name}.wrl"'

214 )

215 else:

216 path_name = f"{footprint_info.model_dir}/{footprint_info.footprint_name}.wrl"

217

218 translationX = (translationX - footprint_info.origin[0]) / 100

219 translationY = -(translationY - footprint_info.origin[1]) / 100

220 translationZ = float(translationZ) / 100

221

222 # Check if a model has already been added to the footprint to prevent duplicates

223 if any(isinstance(child, Model) for child in kicad_mod.getAllChilds()):

224 logging.info("WRL model created at {filename}")

225 logging.info(

226 "WRL model was not added to the footprint to prevent duplicates with STEP "

227 "model"

228 )

229 else:

230 kicad_mod.append(

231 Model(

232 filename=path_name,

233 at=[translationX, translationY, translationZ],

234 rotate=[-float(axis_rotation) for axis_rotation in rotation.split(",")],

235 )

236 )

237 logging.info(f"added {path_name} to footprint")

238

239

240 def ensure_footprint_lib_directories_exist(footprint_info):

241 if not os.path.exists(

242 f"{footprint_info.output_dir}/{footprint_info.footprint_lib}"

243 ):

244 os.makedirs(f"{footprint_info.output_dir}/{footprint_info.footprint_lib}")

245

246 if not os.path.exists(

247 f"{footprint_info.output_dir}/{footprint_info.footprint_lib}/{footprint_info.model_dir}"

248 ):

249 os.makedirs(

250 f"{footprint_info.output_dir}/{footprint_info.footprint_lib}/{footprint_info.model_dir}"

251 )