hellen-one/bin/create_3d_components.py

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!")