98 lines
3.4 KiB
Python
98 lines
3.4 KiB
Python
#!/usr/bin/env python
|
|
############################################################################################
|
|
# Hellen-One: A script to create a 3D components view (VRML).
|
|
# (c) andreika <prometheus.pcb@gmail.com>
|
|
############################################################################################
|
|
|
|
import os, sys, re, math
|
|
import configparser
|
|
import gzip
|
|
|
|
### Unfortunately PyVRML97 (vrml.vrml97) is not capable of properly processing Altium-exported VRML-files...
|
|
|
|
if len(sys.argv) < 3:
|
|
print ("Error! Please specify the place+board files and vrml filename.")
|
|
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 = []
|
|
with open(mergePlaceFile, 'rt') as fmp:
|
|
for line in fmp:
|
|
m = line.split()
|
|
name_and_rot = re.match(r'^(.*?)(\*rotated([\-0-9]+))?(\*flipped([V]))?$', m[0]) # get the name and rotation/flip parts
|
|
if not name_and_rot:
|
|
print ("Unknown place format for " + m[0])
|
|
sys.exit(2)
|
|
name = name_and_rot.group(1)
|
|
rot = name_and_rot.group(3) if name_and_rot.group(3) else "0"
|
|
flip = name_and_rot.group(5) if name_and_rot.group(5) else ""
|
|
m = {"name": name, "x": m[1], "y": m[2], "rot": rot, "flip": flip, "path": config[name]["Prefix"] }
|
|
fragments.append(m)
|
|
|
|
print ("* Starting merge of " + str(len(fragments)) + " board fragments...")
|
|
|
|
outf = gzip.open(fileOutName, 'wt')
|
|
outf.write("#VRML V2.0 utf8\n")
|
|
|
|
pat_hdr = re.compile('^#VRML.*')
|
|
pat_idx = re.compile(r'(DEF|USE)\s+(Shape|_)(\w)')
|
|
pat_kicad_transform = re.compile(r'DEF\s+(\w+)\s+Transform.*')
|
|
|
|
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
|
|
rot = float(frag["rot"])
|
|
invertZ = -1.0 if (frag["flip"] == "V") else 1.0
|
|
fileName = frag["path"] + "-vrml.wrl"
|
|
was_global_transform = False
|
|
fragId = str(fId).zfill(2)
|
|
|
|
print ("* Adding " + frag["name"] + " (" + fileName + ") at (" + str(off_x_mm) + "," + str(off_y_mm) + "), rot=" + str(rot) + ", invZ=" + str(invertZ) + "...")
|
|
with open(fileName, 'rt') as f:
|
|
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
|
|
# add global transformation for the module
|
|
if not was_global_transform:
|
|
z_offset = 0
|
|
# todo: the board is 1.6 mm thick?
|
|
board_thickness = 1.6
|
|
# 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'
|
|
z_offset = -(board_thickness / 2)
|
|
# for upside-down modules, the offset needs to be reversed
|
|
if (invertZ < 0):
|
|
z_offset = -board_thickness - z_offset
|
|
outf.write("DEF TX" + frag["name"].replace('-', '') + " Transform {\n")
|
|
outf.write(" center 0 0 0\n")
|
|
outf.write(" rotation 0 0 1 " + str(math.radians(rot)) + "\n")
|
|
outf.write(" scale 1.0 " + str(invertZ) + " " + str(invertZ) + "\n")
|
|
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
|
|
|
|
line = pat_idx.sub(r'\g<1> \g<2>' + fragId + '\g<3>', line)
|
|
outf.write(line + "\n")
|
|
f.close()
|
|
if was_global_transform:
|
|
outf.write("] }\n")
|
|
fId = fId + 1
|
|
|
|
outf.close()
|
|
|
|
print ("Done!")
|