2021-02-14 08:51:14 -08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
############################################################################################
|
|
|
|
# Hellen-One: A script to create a 3D components view (VRML).
|
|
|
|
# (c) andreika <prometheus.pcb@gmail.com>
|
|
|
|
############################################################################################
|
|
|
|
|
2021-05-19 03:07:18 -07:00
|
|
|
import os, sys, re, math
|
2021-02-14 08:51:14 -08:00
|
|
|
import configparser
|
|
|
|
import gzip
|
|
|
|
|
|
|
|
### Unfortunately PyVRML97 (vrml.vrml97) is not capable of properly processing Altium-exported VRML-files...
|
|
|
|
|
|
|
|
if len(sys.argv) < 3:
|
2021-05-19 03:07:18 -07:00
|
|
|
print ("Error! Please specify the place+board files and vrml filename.")
|
2021-02-14 08:51:14 -08:00
|
|
|
sys.exit(1)
|
|
|
|
mergePlaceFile = sys.argv[1]
|
|
|
|
mergeBoardFile = sys.argv[2]
|
|
|
|
fileOutName = sys.argv[3]
|
|
|
|
|
|
|
|
# read board config
|
|
|
|
config = configparser.ConfigParser()
|
|
|
|
config.read(mergeBoardFile)
|
|
|
|
|
|
|
|
# read place file
|
|
|
|
fragments = []
|
2022-03-26 12:30:24 -07:00
|
|
|
with open(mergePlaceFile, 'rt') as fmp:
|
2021-02-14 08:51:14 -08:00
|
|
|
for line in fmp:
|
|
|
|
m = line.split()
|
2021-10-02 09:07:50 -07:00
|
|
|
name_and_rot = re.split(r'\*rotated|\*flipped', m[0]) # split the name and rotation/flip parts
|
2021-03-21 03:34:52 -07:00
|
|
|
name = name_and_rot[0]
|
|
|
|
rot = name_and_rot[1] if len(name_and_rot) > 1 else "0"
|
2021-10-02 09:07:50 -07:00
|
|
|
flip = name_and_rot[2] if len(name_and_rot) > 2 else ""
|
|
|
|
m = {"name": name, "x": m[1], "y": m[2], "rot": rot, "flip": flip, "path": config[name]["Prefix"] }
|
2021-02-14 08:51:14 -08:00
|
|
|
fragments.append(m)
|
|
|
|
|
2021-05-19 03:07:18 -07:00
|
|
|
print ("* Starting merge of " + str(len(fragments)) + " board fragments...")
|
2021-02-14 08:51:14 -08:00
|
|
|
|
2022-03-26 12:30:24 -07:00
|
|
|
outf = gzip.open(fileOutName, 'wt')
|
2021-02-14 08:51:14 -08:00
|
|
|
outf.write("#VRML V2.0 utf8\n")
|
|
|
|
|
|
|
|
pat_hdr = re.compile('^#VRML.*')
|
|
|
|
pat_idx = re.compile(r'(DEF|USE)\s+(Shape|_)(\w)')
|
2021-05-19 03:07:18 -07:00
|
|
|
pat_kicad_transform = re.compile(r'DEF\s+(\w+)\s+Transform.*')
|
2021-02-14 08:51:14 -08:00
|
|
|
|
|
|
|
fId = 1
|
|
|
|
for frag in fragments:
|
|
|
|
# convert to mm
|
|
|
|
off_x_mm = float(frag["x"]) * 25.4
|
|
|
|
off_y_mm = float(frag["y"]) * 25.4
|
2021-03-21 03:34:52 -07:00
|
|
|
rot = float(frag["rot"])
|
2021-10-02 09:07:50 -07:00
|
|
|
invertZ = -1.0 if (frag["flip"] == "V") else 1.0
|
2021-02-14 08:51:14 -08:00
|
|
|
fileName = frag["path"] + "-vrml.wrl"
|
2021-05-19 03:07:18 -07:00
|
|
|
was_global_transform = False
|
|
|
|
fragId = str(fId).zfill(2)
|
|
|
|
|
2021-10-02 09:07:50 -07:00
|
|
|
print ("* Adding " + frag["name"] + " (" + fileName + ") at (" + str(off_x_mm) + "," + str(off_y_mm) + "), rot=" + str(rot) + ", invZ=" + str(invertZ) + "...")
|
2022-03-26 12:30:24 -07:00
|
|
|
with open(fileName, 'rt') as f:
|
2021-02-14 08:51:14 -08:00
|
|
|
for line in f:
|
|
|
|
line = line.rstrip()
|
|
|
|
# skip the headers (we write our own because there should be only 1 header)
|
|
|
|
if pat_hdr.match(line):
|
|
|
|
continue
|
2021-05-19 03:07:18 -07:00
|
|
|
# add global transformation for the module
|
|
|
|
if not was_global_transform:
|
2021-10-02 09:07:50 -07:00
|
|
|
z_offset = 0
|
|
|
|
# todo: the board is 1.6 mm thick?
|
|
|
|
board_thickness = 1.6
|
2021-05-19 03:07:18 -07:00
|
|
|
# for bottom-aligned kicad VRML files, we need to shift them down - to align it with the top surface of the board
|
|
|
|
if pat_kicad_transform.match(line):
|
|
|
|
print ("* Kicad VRML detected!")
|
|
|
|
# todo: this is a 'hack'
|
2022-03-26 12:30:24 -07:00
|
|
|
z_offset = -(board_thickness / 2)
|
2021-10-02 09:07:50 -07:00
|
|
|
# for upside-down modules, the offset needs to be reversed
|
|
|
|
if (invertZ < 0):
|
|
|
|
z_offset = -board_thickness - z_offset
|
2021-05-28 02:49:59 -07:00
|
|
|
outf.write("DEF TX" + frag["name"].replace('-', '') + " Transform {\n")
|
2021-05-19 03:07:18 -07:00
|
|
|
outf.write(" center 0 0 0\n")
|
|
|
|
outf.write(" rotation 0 0 1 " + str(math.radians(rot)) + "\n")
|
2021-10-02 09:07:50 -07:00
|
|
|
outf.write(" scale 1.0 " + str(invertZ) + " " + str(invertZ) + "\n")
|
2021-05-19 03:07:18 -07:00
|
|
|
outf.write(" scaleOrientation 0 0 1 0\n")
|
|
|
|
outf.write(" translation " + str(off_x_mm) + " " + str(off_y_mm) + " " + str(z_offset) + "\n")
|
|
|
|
outf.write(" children [\n")
|
|
|
|
was_global_transform = True
|
|
|
|
|
2021-02-14 08:51:14 -08:00
|
|
|
line = pat_idx.sub(r'\g<1> \g<2>' + fragId + '\g<3>', line)
|
|
|
|
outf.write(line + "\n")
|
|
|
|
f.close()
|
2021-05-19 03:07:18 -07:00
|
|
|
if was_global_transform:
|
|
|
|
outf.write("] }\n")
|
2021-02-14 08:51:14 -08:00
|
|
|
fId = fId + 1
|
|
|
|
|
|
|
|
outf.close()
|
|
|
|
|
2021-05-19 03:07:18 -07:00
|
|
|
print ("Done!")
|