Major update: Python 3.x migration + new board render scripts!

This commit is contained in:
andreika-git 2022-03-26 21:30:24 +02:00
parent 42c2c0b38e
commit 4c5befa140
19 changed files with 489 additions and 463 deletions

View File

@ -3,13 +3,13 @@
echo "Checking the environment..." echo "Checking the environment..."
unameOut="$(uname -s)" unameOut="$(uname -s)"
case "${unameOut}" in case "${unameOut}" in
Linux*) machine=linux;; Linux*) machine=linux;;
Darwin*) machine=mac;; Darwin*) machine=mac;;
CYGWIN*) machine=cygwin;; CYGWIN*) machine=cygwin;;
MSYS*) machine=msys;; MSYS*) machine=msys;;
MINGW32*) machine=mingw32;; MINGW32*) machine=mingw32;;
MINGW64*) machine=mingw64;; MINGW64*) machine=mingw64;;
*) machine=unknown;; *) machine=unknown;;
esac esac
if [ "${machine}" = "unknown" ] ; then if [ "${machine}" = "unknown" ] ; then
echo "* Warning! Unknown environment: ${unameOut}" echo "* Warning! Unknown environment: ${unameOut}"
@ -43,17 +43,17 @@ function install_package {
# we give it one more chance and try to download the installer # we give it one more chance and try to download the installer
echo "Do you want to download the cygwin package manager (apt-cyg) and install the required utilities? (Press 1 or 2)" echo "Do you want to download the cygwin package manager (apt-cyg) and install the required utilities? (Press 1 or 2)"
select yn in "Yes" "No"; do select yn in "Yes" "No"; do
case $yn in case $yn in
Yes ) Yes )
url="rawgit.com/transcode-open/apt-cyg/master/apt-cyg" url="rawgit.com/transcode-open/apt-cyg/master/apt-cyg"
dst="/tmp/apt-cyg" dst="/tmp/apt-cyg"
download_url $url $dst download_url $url $dst
install $dst /bin install $dst /bin
rm $dst rm $dst
break;; break;;
No ) No )
echo "Please install it manually using you package manager!" >&2 echo "Please install it manually using you package manager!" >&2
exit 1; exit 1;
esac esac
done done
else else
@ -63,19 +63,19 @@ function install_package {
elif [ "${machine}" = "msys" ] ; then elif [ "${machine}" = "msys" ] ; then
if [ ! -x "$(command -v pacman)" ] ; then if [ ! -x "$(command -v pacman)" ] ; then
echo "Cannot detect pacman manager. Please install it manually using you package manager!" >&2 echo "Cannot detect pacman manager. Please install it manually using you package manager!" >&2
exit 1; exit 1;
fi fi
fi fi
# now install # now install
echo "Do you want to install '$1' now? (Press 1 or 2)" echo "Do you want to install '$1' now? (Press 1 or 2)"
select yn in "Yes" "No"; do select yn in "Yes" "No"; do
case $yn in case $yn in
Yes ) Yes )
break;; break;;
No ) No )
echo "Please install it manually using your package manager!" >&2 echo "Please install it manually using your package manager!" >&2
exit 1; exit 1;
esac esac
done done
@ -89,23 +89,23 @@ function install_package {
sudo apt update sudo apt update
if ! sudo apt-get install $1; then if ! sudo apt-get install $1; then
echo "Error! Cannot install the package $1. Please install it manually using your package manager!" >&2 echo "Error! Cannot install the package $1. Please install it manually using your package manager!" >&2
exit 1; exit 1;
fi fi
fi fi
fi fi
} }
echo "Checking the Python version..." echo "Checking the Python version..."
# check python version - should be 2.x ONLY # check python version - should be 3.x ONLY
python_bin="python2.7" python_bin="python3"
while true; do while true; do
python_ver=$($python_bin -V 2>&1 | grep -Po '(?<=Python )(.+)') python_ver=$($python_bin -V 2>&1 | grep -Po '(?<=Python )(.+)')
if [[ -z "$python_ver" ]] || [[ ! $python_ver =~ ^2\.7.* ]] ; then if [[ -z "$python_ver" ]] || [[ ! $python_ver =~ ^3\.[56789].* ]] ; then
echo "Error! Python 2.7.x is required. It should be installed and added to the PATH!" echo "Error! Python 3.5 or later is required. It should be installed and added to the PATH!"
install_package python2 install_package python2
if [ "${machine}" = "linux" ] ; then if [ "${machine}" = "linux" ] ; then
install_package python2-dev install_package python2-dev
fi fi
else else
break break
fi fi
@ -116,10 +116,10 @@ function check_library {
echo "* Checking $1..." echo "* Checking $1..."
while true; do while true; do
if ! command -v pkg-config >/dev/null 2>&1 ; then if ! command -v pkg-config >/dev/null 2>&1 ; then
echo "* Missing pkg-config" echo "* Missing pkg-config"
echo "Do you want to download and install it now? (Press 1 or 2)" echo "Do you want to download and install it now? (Press 1 or 2)"
select yn in "Yes" "No"; do select yn in "Yes" "No"; do
case $yn in case $yn in
Yes ) install_package pkg-config; break;; Yes ) install_package pkg-config; break;;
No ) exit 1; No ) exit 1;
esac esac
@ -137,35 +137,35 @@ function check_library {
done done
} }
function install_pip2 { function install_pip3 {
pip2installer="https://bootstrap.pypa.io/pip/2.7/get-pip.py" pip3installer="https://bootstrap.pypa.io/pip/3.6/get-pip.py"
dst="/tmp/get-pip.py" dst="/tmp/get-pip.py"
download_url $pip2installer $dst download_url $pip3installer $dst
$python_bin $dst $python_bin $dst
rm $dst rm $dst
} }
function pip2_install_module { function pip3_install_module {
# check if pip2 works # check if pip3 works
pip_bin="$python_bin -m pip" pip_bin="$python_bin -m pip"
while true; do while true; do
pip2v=$($pip_bin --version 2>&1 | grep -Po '(pip [0-9]+\.[0-9]+.*)') pip3v=$($pip_bin --version 2>&1 | grep -Po '(pip [0-9]+\.[0-9]+.*)')
if [[ -z "$pip2v" ]] ; then if [[ -z "$pip3v" ]] ; then
echo "* Missing pip2" echo "* Missing pip3"
if [ "${machine}" = "linux" ] ; then if [ "${machine}" = "linux" ] ; then
echo "Do you want to download and install it now? (Press 1 or 2)" echo "Do you want to download and install it now? (Press 1 or 2)"
select yn in "Yes" "No"; do select yn in "Yes" "No"; do
case $yn in case $yn in
Yes ) install_pip2; break;; Yes ) install_pip3; break;;
No ) exit 1; No ) exit 1;
esac esac
done done
else else
install_package pip2 install_package pip3
fi fi
else else
break break
fi fi
done done
# check if gnu compiler works (needed by some modules) # check if gnu compiler works (needed by some modules)
@ -173,14 +173,14 @@ function pip2_install_module {
while true; do while true; do
gccv=$($gcc_bin -v 2>&1 | grep -Po '(version\s+[0-9]+\.[0-9]+.*)') gccv=$($gcc_bin -v 2>&1 | grep -Po '(version\s+[0-9]+\.[0-9]+.*)')
if [[ -z "$gccv" ]] ; then if [[ -z "$gccv" ]] ; then
echo "* Missing gcc compiler" echo "* Missing gcc compiler"
install_package gcc install_package gcc
else else
break break
fi fi
done done
echo "* Installing python module $1 using $pip2v and gcc $gccv..." echo "* Installing python module $1 using $pip3v and gcc $gccv..."
$pip_bin install $pymodule $pip_bin install $pymodule
} }
@ -192,8 +192,8 @@ git_ver=0
while true; do while true; do
git_ver=$($git_bin version 2>&1 | grep -Po '(version [0-9]+\.[0-9]+.*)') git_ver=$($git_bin version 2>&1 | grep -Po '(version [0-9]+\.[0-9]+.*)')
if [[ -z "$git_ver" ]] ; then if [[ -z "$git_ver" ]] ; then
echo "Error! Git not found! We need it to update submodules." echo "Error! Git not found! We need it to update submodules."
install_package git install_package git
else else
break break
fi fi
@ -201,11 +201,16 @@ done
echo "* Git $git_ver detected!" echo "* Git $git_ver detected!"
echo "Updating git submodules for scripts..." echo "Updating git submodules for scripts..."
git submodule update --init -- bin/gerbmerge bin/python-combine-pdfs bin/InteractiveHtmlBom bin/pcb-tools #git submodule update --init -- bin/gerbmerge bin/python-combine-pdfs bin/InteractiveHtmlBom bin/pcb-tools
echo "Checking the Python modules..." echo "Checking the Python modules..."
declare -A modules declare -A modules
modules[simpleparse]=simpleparse modules[simpleparse]=simpleparse
modules[moderngl]=ModernGL
modules[PIL]=Pillow
modules[pyrr]=Pyrr
modules[numpy]=NumPy
modules[vrml.vrml97]=PyVRML97
modules[contextlib2]=contextlib2 modules[contextlib2]=contextlib2
modules[PyPDF2]=PyPDF2 modules[PyPDF2]=PyPDF2
modules[gerber]=gerber modules[gerber]=gerber
@ -219,13 +224,13 @@ for module in "${!modules[@]}"; do
while true; do while true; do
$python_bin -c "import sys, pkgutil; sys.path.append('./bin/pcb-tools'); sys.exit(0 if (pkgutil.find_loader('$module')) else 1)" $python_bin -c "import sys, pkgutil; sys.path.append('./bin/pcb-tools'); sys.exit(0 if (pkgutil.find_loader('$module')) else 1)"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "* Checking Python module '$pymodule': OK" echo "* Checking Python module '$pymodule': OK"
break break
else else
echo "* Checking Python module '$pymodule': ERROR!" echo "* Checking Python module '$pymodule': ERROR!"
echo " Python module '$pymodule' is required and NOT found!" echo " Python module '$pymodule' is required and NOT found!"
# some modules have dependencies # some modules have dependencies
if [ "$pymodule" = "cairocffi" ]; then if [ "$pymodule" = "cairocffi" ]; then
if [ "${machine}" = "linux" ] ; then if [ "${machine}" = "linux" ] ; then
devname="dev" devname="dev"
@ -236,15 +241,15 @@ for module in "${!modules[@]}"; do
check_library cairo cairo libcairo-${devname} check_library cairo cairo libcairo-${devname}
fi fi
if [ ]; then if [ ]; then
echo "Please use 'pip2 install $pymodule' to install it manually!" echo "Please use 'pip3 install $pymodule' to install it manually!"
exit 1; exit 1;
else else
echo "Do you want to download and install it now? (Press 1 or 2)" echo "Do you want to download and install it now? (Press 1 or 2)"
select yn in "Yes" "No"; do select yn in "Yes" "No"; do
case $yn in case $yn in
Yes ) pip2_install_module $pymodule; break;; Yes ) pip3_install_module $pymodule; break;;
No ) exit 1; No ) exit 1;
esac esac
done done
fi fi
@ -252,60 +257,6 @@ for module in "${!modules[@]}"; do
done done
done done
echo "Checking if Node.js is installed..."
node_bin="node"
node_ver=0
while true; do
node_ver=$($node_bin -v 2>&1 | grep -Po '(v[0-9]+.*)')
if [[ -z "$node_ver" ]] ; then
echo "Error! This script requires Node.Js installed in PATH!"
if [ "${machine}" = "linux" ] ; then
install_package nodejs
else
echo "Please download and install it from here: https://nodejs.org/en/download/"
exit 1
fi
else
break
fi
done
echo "* Node.js $node_ver detected!"
echo "Checking npm..."
npm_bin="npm"
npm_ver=0
while true; do
npm_ver=$($npm_bin -v 2>&1 | grep -Po '([0-9]+\.[0-9]+.*)')
if [[ -z "$npm_ver" ]] ; then
echo "Error! NPM not found! We need it to check Node.Js packages."
install_package npm
else
break
fi
done
echo "* NPM $npm_ver detected!"
echo "Checking Node.js packages..."
pushd ./bin/render_vrml > /dev/null
for package in 'puppeteer' 'pngjs' 'fs' 'zlib'; do
if [ `npm list --depth=0 | grep -c "${package}@"` -eq 1 ]; then
echo "* Checking Node.js module '$package': OK"
else
echo "* Checking Node.js module '$package': ERROR!"
echo " The module '$package' is required and NOT found! Please use 'npm install $package' to install it"
echo "Do you want to download and install it now? (Press 1 or 2)"
select yn in "Yes" "No"; do
case $yn in
Yes ) npm install $package --no-shrinkwrap; break;;
No ) exit 1;
esac
done
fi
done
popd > /dev/null
echo "All checks done!" echo "All checks done!"
exit 0 exit 0

View File

@ -23,7 +23,7 @@ config.read(mergeBoardFile)
# read place file # read place file
fragments = [] fragments = []
with open(mergePlaceFile, 'rb') as fmp: with open(mergePlaceFile, 'rt') as fmp:
for line in fmp: for line in fmp:
m = line.split() m = line.split()
name_and_rot = re.split(r'\*rotated|\*flipped', m[0]) # split the name and rotation/flip parts name_and_rot = re.split(r'\*rotated|\*flipped', m[0]) # split the name and rotation/flip parts
@ -35,7 +35,7 @@ with open(mergePlaceFile, 'rb') as fmp:
print ("* Starting merge of " + str(len(fragments)) + " board fragments...") print ("* Starting merge of " + str(len(fragments)) + " board fragments...")
outf = gzip.open(fileOutName, 'wb') outf = gzip.open(fileOutName, 'wt')
outf.write("#VRML V2.0 utf8\n") outf.write("#VRML V2.0 utf8\n")
pat_hdr = re.compile('^#VRML.*') pat_hdr = re.compile('^#VRML.*')
@ -54,7 +54,7 @@ for frag in fragments:
fragId = str(fId).zfill(2) 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) + "...") print ("* Adding " + frag["name"] + " (" + fileName + ") at (" + str(off_x_mm) + "," + str(off_y_mm) + "), rot=" + str(rot) + ", invZ=" + str(invertZ) + "...")
with open(fileName, 'rb') as f: with open(fileName, 'rt') as f:
for line in f: for line in f:
line = line.rstrip() line = line.rstrip()
# skip the headers (we write our own because there should be only 1 header) # skip the headers (we write our own because there should be only 1 header)
@ -69,7 +69,7 @@ for frag in fragments:
if pat_kicad_transform.match(line): if pat_kicad_transform.match(line):
print ("* Kicad VRML detected!") print ("* Kicad VRML detected!")
# todo: this is a 'hack' # todo: this is a 'hack'
z_offset = -board_thickness z_offset = -(board_thickness / 2)
# for upside-down modules, the offset needs to be reversed # for upside-down modules, the offset needs to be reversed
if (invertZ < 0): if (invertZ < 0):
z_offset = -board_thickness - z_offset z_offset = -board_thickness - z_offset

View File

@ -11,7 +11,7 @@ frame_rev="$3"
bom_replace="$4" bom_replace="$4"
comp_img_offset="$5" comp_img_offset="$5"
python_bin="python2.7" python_bin="python3"
############################################################################################ ############################################################################################

View File

@ -12,7 +12,7 @@ frame_rev="$4"
bom_replace="$5" bom_replace="$5"
comp_img_offset="$6" comp_img_offset="$6"
python_bin="python2.7" python_bin="python3"
############################################################################################ ############################################################################################

View File

@ -24,7 +24,7 @@ sys.path.append("./bin/InteractiveHtmlBom/InteractiveHtmlBom/core")
from lzstring import LZString from lzstring import LZString
if len(sys.argv) < 12: if len(sys.argv) < 12:
print "Error! Please specify all the parameters!" print ("Error! Please specify all the parameters!")
sys.exit(1) sys.exit(1)
boardName = sys.argv[1] boardName = sys.argv[1]
@ -45,7 +45,7 @@ inch_to_mm = 25.4
def getFormat(xI, xD, yI, yD, yInvert): def getFormat(xI, xD, yI, yD, yInvert):
print "* Format: ", xI, xD, yI, yD print ("* Format: ", xI, xD, yI, yD)
fmt_pat_x = re.compile(r'^([0-9]{'+xI+'})([0-9]{'+xD+'})$') fmt_pat_x = re.compile(r'^([0-9]{'+xI+'})([0-9]{'+xD+'})$')
fmt_pat_y = re.compile(r'^([0-9]{'+yI+'})([0-9]{'+yD+'})$') fmt_pat_y = re.compile(r'^([0-9]{'+yI+'})([0-9]{'+yD+'})$')
return [fmt_pat_x, fmt_pat_y, yInvert] return [fmt_pat_x, fmt_pat_y, yInvert]
@ -85,7 +85,7 @@ def readGerber(filePath, yInvert):
maxCoord = [ -99999, -99999 ] maxCoord = [ -99999, -99999 ]
format = getFormat("2", "5", "2", "5", yInvert) format = getFormat("2", "5", "2", "5", yInvert)
invertedMask = False invertedMask = False
with open(filePath, 'rb') as f: with open(filePath, 'rt') as f:
for line in f: for line in f:
line = line.strip() line = line.strip()
#print line #print line
@ -101,7 +101,7 @@ def readGerber(filePath, yInvert):
apertNum = int(apertCircle.group(1)) apertNum = int(apertCircle.group(1))
apertSize = apertCircle.group(2) apertSize = apertCircle.group(2)
apertList[apertNum] = ["circle", apertSize] apertList[apertNum] = ["circle", apertSize]
# print "* Aperture C: " + str(apertNum) + " = " + apertSize # print ("* Aperture C: " + str(apertNum) + " = " + apertSize)
apertRect = apert_rect_pat.match(line) apertRect = apert_rect_pat.match(line)
if apertRect: if apertRect:
@ -109,7 +109,7 @@ def readGerber(filePath, yInvert):
apertSizeX = apertRect.group(2) apertSizeX = apertRect.group(2)
apertSizeY = apertRect.group(4) apertSizeY = apertRect.group(4)
apertList[apertNum] = ["rect", [apertSizeX, apertSizeY]] apertList[apertNum] = ["rect", [apertSizeX, apertSizeY]]
# print "* Aperture R: " + str(apertNum) + " = " + apertSizeX + " " + apertSizeY # print ("* Aperture R: " + str(apertNum) + " = " + apertSizeX + " " + apertSizeY)
op = op_pat.match(line) op = op_pat.match(line)
if op: if op:
@ -151,7 +151,7 @@ def readGerber(filePath, yInvert):
a = int(op) a = int(op)
cur_aper_type = apertList[a][0] cur_aper_type = apertList[a][0]
cur_size = apertList[a][1] cur_size = apertList[a][1]
# print "* Changing aperture: ", a # print ("* Changing aperture: ", a)
else: else:
cur_x = x cur_x = x
cur_y = y cur_y = y
@ -187,7 +187,7 @@ def readFootprint(fpname, footprintsPath, des):
pat_pad = re.compile(r'^\s*\(pad\s+\"?([0-9A-Z]+)\"?\s+(\w+)\s+(\w+)\s+\(at\s+([+\-0-9e\.]+)\s+([+\-0-9e\.]+)\s*([+\-0-9\.]+)?\)\s+\(size\s+([+\-0-9\.]+)\s+([+\-0-9\.]+)\)(\s*\(drill\s+([+\-0-9\.]+)\))?\s+\(layer[s]?\s+\"?([^\)]+)\)(\s*\(roundrect_rratio\s+([+\-0-9\.]+)\))?') pat_pad = re.compile(r'^\s*\(pad\s+\"?([0-9A-Z]+)\"?\s+(\w+)\s+(\w+)\s+\(at\s+([+\-0-9e\.]+)\s+([+\-0-9e\.]+)\s*([+\-0-9\.]+)?\)\s+\(size\s+([+\-0-9\.]+)\s+([+\-0-9\.]+)\)(\s*\(drill\s+([+\-0-9\.]+)\))?\s+\(layer[s]?\s+\"?([^\)]+)\)(\s*\(roundrect_rratio\s+([+\-0-9\.]+)\))?')
fpFileName = footprintsPath + "/" + fpname + ".kicad_mod" fpFileName = footprintsPath + "/" + fpname + ".kicad_mod"
print("* Reading " + fpFileName) print ("* Reading " + fpFileName)
if not os.path.isfile(fpFileName): if not os.path.isfile(fpFileName):
print("* Error! Footprint NOT FOUND! Skipping " + des) print("* Error! Footprint NOT FOUND! Skipping " + des)
return None return None
@ -262,13 +262,13 @@ def readFootprints(bomPath, cplPath, footprintsPath, yInvert):
footprints = {} footprints = {}
rotations = {} rotations = {}
# read rotations csv (to undo weird JLC's angles which are not footprint-oriented) # read rotations csv (to undo weird JLC's angles which are not footprint-oriented)
with open(fixRotationsPath, 'rb') as f: with open(fixRotationsPath, 'rt') as f:
next(f) next(f)
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
for row in reader: for row in reader:
rotations[row[0]] = float(row[1]) rotations[row[0]] = float(row[1])
# read BOM csv # read BOM csv
with open(bomPath, 'rb') as f: with open(bomPath, 'rt') as f:
next(f) next(f)
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
for row in reader: for row in reader:
@ -281,7 +281,7 @@ def readFootprints(bomPath, cplPath, footprintsPath, yInvert):
for b in bb: for b in bb:
bom[b] = { "fp": row[2], "idx": idx } bom[b] = { "fp": row[2], "idx": idx }
# read CPL csv # read CPL csv
with open(cplPath, 'rb') as f: with open(cplPath, 'rt') as f:
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
for row in reader: for row in reader:
if row[0] in bom: if row[0] in bom:
@ -340,7 +340,7 @@ def readFootprints(bomPath, cplPath, footprintsPath, yInvert):
################################################################### ###################################################################
with open(htmlFileName, 'rb') as f: with open(htmlFileName, 'rt') as f:
html = f.read() html = f.read()
f.close() f.close()
@ -382,7 +382,7 @@ print("* Adding the pcb image...")
with open(renderedPcbPath, mode='rb') as f: with open(renderedPcbPath, mode='rb') as f:
renderedPcb = f.read() renderedPcb = f.read()
html = html.replace('___PCBDPI___', renderedPcbDpi) html = html.replace('___PCBDPI___', renderedPcbDpi)
html = html.replace('___PCBIMAGE___', 'data:image/png;base64,' + base64.b64encode(renderedPcb)) html = html.replace('___PCBIMAGE___', 'data:image/png;base64,' + base64.b64encode(renderedPcb).decode('ascii'))
print("* Adding the BOM data...") print("* Adding the BOM data...")
jsonBase64 = LZString().compress_to_base64(jsonText) jsonBase64 = LZString().compress_to_base64(jsonText)
@ -394,5 +394,5 @@ with open(iBomFilePath, "wt") as wf:
wf.write(html) wf.write(html)
wf.close() wf.close()
print "Done!" print ("Done!")

@ -1 +1 @@
Subproject commit 3404d39dd773b8f5c745df547bcd812336f1621d Subproject commit f78db83293e9737d4ee4e0d5d377e908ba9aba91

View File

@ -11,7 +11,7 @@ include_pat = re.compile(r'#include\s+\"?([^\"]+)\"?$')
def read_repl_file(csv_name, repl_base_path, replList): def read_repl_file(csv_name, repl_base_path, replList):
print ("Reading replacement list from the CSV file " + csv_name + "...") print ("Reading replacement list from the CSV file " + csv_name + "...")
with open(csv_name, 'rb') as f: with open(csv_name, 'rt') as f:
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
for row in reader: for row in reader:
# skip empty lines # skip empty lines
@ -43,7 +43,7 @@ print ("Opening BOM file " + fileName + "...")
rows = OrderedDict() rows = OrderedDict()
emptyId = 1 emptyId = 1
with open(fileName, 'rb') as f: with open(fileName, 'rt') as f:
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
print ("Searching for duplicates...") print ("Searching for duplicates...")
for row in reader: for row in reader:
@ -75,7 +75,7 @@ read_repl_file(repl_csv, repl_base_path, replList)
print ("Processing the board replacements...") print ("Processing the board replacements...")
for r in replList: for r in replList:
reDesignator = r[0] reDesignator = r[0]
for rowName in rows: for rowName in list(rows.keys()):
row = rows[rowName] row = rows[rowName]
if reDesignator in row[1]: if reDesignator in row[1]:
print ("* Removing " + reDesignator + " from the old row...") print ("* Removing " + reDesignator + " from the old row...")
@ -110,15 +110,15 @@ for rowName in rows:
print ("Saving...") print ("Saving...")
with open (fileName, 'wb') as new_f: with open (fileName, 'wt') as new_f:
rowIdx = 0 rowIdx = 0
for rowName in rows: for rowName in rows:
#for idx,item in enumerate(rows[rowName]): #for idx,item in enumerate(rows[rowName]):
# print idx , ": ", item # print idx , ": ", item
if rowIdx == 0: if rowIdx == 0:
writer = csv.writer(new_f, quoting=csv.QUOTE_NONE, quotechar='"', delimiter=',', lineterminator='\n') writer = csv.writer(new_f, quoting=csv.QUOTE_NONE, quotechar='"', escapechar='', delimiter=',', lineterminator='\n')
elif rowIdx == 1: elif rowIdx == 1:
writer = csv.writer(new_f, quoting=csv.QUOTE_ALL, quotechar='"', delimiter=',', lineterminator='\n') writer = csv.writer(new_f, quoting=csv.QUOTE_ALL, quotechar='"', escapechar='', delimiter=',', lineterminator='\n')
row = rows[rowName] row = rows[rowName]
# restore empty names # restore empty names
if rowName[0] == '_': if rowName[0] == '_':

View File

@ -89,7 +89,8 @@ def print_module(name, prefixPath, moduleName, fileName, isBoard, isBottom):
"*" + bottom + "Soldermask=%(prefix)s.GBS", "*" + bottom + "Soldermask=%(prefix)s.GBS",
"*" + bottom + "Silkscreen=%(prefix)s.GBO", "*" + bottom + "Silkscreen=%(prefix)s.GBO",
"*Keepout=%(prefix)s.GKO", "*Keepout=%(prefix)s.GKO",
"Drills=%(prefix)s.DRL"]) "Drills=%(prefix)s.DRL",
"drillspth=%(prefix)s.DRL"])
if ((os.path.isfile(prefix + ".G1") and os.path.isfile(prefix + ".G2")) or isBoard == 1): if ((os.path.isfile(prefix + ".G1") and os.path.isfile(prefix + ".G2")) or isBoard == 1):
write_lines(file, [ write_lines(file, [
"*InnerLayer2=%(prefix)s.G1", "*InnerLayer2=%(prefix)s.G1",
@ -105,7 +106,7 @@ def print_module(name, prefixPath, moduleName, fileName, isBoard, isBottom):
def append_cpl(src_fname, dst_fname, x, y, mrot, isBottom, suffix = ""): def append_cpl(src_fname, dst_fname, x, y, mrot, isBottom, suffix = ""):
print ("* appending the CPL with offset (" + str(x) + "," + str(y) + ")...") print ("* appending the CPL with offset (" + str(x) + "," + str(y) + ")...")
with open(src_fname, 'rb') as src_f, open(dst_fname, 'a') as dst_f: with open(src_fname, 'rt') as src_f, open(dst_fname, 'a') as dst_f:
reader = csv.reader(src_f, delimiter=',') reader = csv.reader(src_f, delimiter=',')
i=0 i=0
# skip header # skip header
@ -153,7 +154,7 @@ def append_cpl(src_fname, dst_fname, x, y, mrot, isBottom, suffix = ""):
def append_bom(src_fname, dst_fname, suffix = ""): def append_bom(src_fname, dst_fname, suffix = ""):
print ("* appending the BOM...") print ("* appending the BOM...")
with open(src_fname, 'rb') as src_f, open(dst_fname, 'a') as dst_f: with open(src_fname, 'rt') as src_f, open(dst_fname, 'a') as dst_f:
reader = csv.reader(src_f, delimiter=',') reader = csv.reader(src_f, delimiter=',')
i = 0 i = 0
# skip header # skip header
@ -332,7 +333,7 @@ p = subprocess.Popen([sys.executable, "bin/gerbmerge/gerbmerge",
board_cfg_path], board_cfg_path],
stdin=subprocess.PIPE) stdin=subprocess.PIPE)
# pass 'y' symbol to the subprocess as if a user pressed 'yes' # pass 'y' symbol to the subprocess as if a user pressed 'yes'
p.communicate(input='y\n')[0] p.communicate(input=b'y\n')[0]
check_returncode(p.returncode) check_returncode(p.returncode)
print ("Post-processing BOM...") print ("Post-processing BOM...")
@ -340,9 +341,9 @@ try:
out = subprocess.check_output([sys.executable, "bin/process_BOM.py", out = subprocess.check_output([sys.executable, "bin/process_BOM.py",
board_bom, board_bom,
bom_replace_csv_path], stderr=subprocess.STDOUT) bom_replace_csv_path], stderr=subprocess.STDOUT)
print (out) print (out.decode('ascii'))
except subprocess.CalledProcessError, e: except subprocess.CalledProcessError as e:
print ("BOM processing error:\n" + e.output) print ("BOM processing error:\n" + e.output.decode('ascii'))
sys.exit(2) sys.exit(2)
print ("Merging Schematics...") print ("Merging Schematics...")
@ -383,14 +384,14 @@ result = subprocess.call([sys.executable, "bin/create_3d_components.py",
check_returncode(result) check_returncode(result)
print ("Rendering a 3D-model of the board components...") print ("Rendering a 3D-model of the board components...")
result = subprocess.call([node_bin, "bin/render_vrml/render_components.js", result = subprocess.call([sys.executable, "bin/render_vrml/render_components.py",
board_misc_path_name + "-3D.wrl.gz", board_misc_path_name + "-3D.wrl.gz",
board_img_components, board_img_components,
imageDpi]) imageDpi])
check_returncode(result) check_returncode(result)
print ("Creating a composite board image...") print ("Creating a composite board image...")
result = subprocess.call([node_bin, "bin/render_vrml/render_board.js", result = subprocess.call([sys.executable, "bin/render_vrml/render_board.py",
board_img_top, board_img_top,
board_img_outline, board_img_outline,
board_img_components, board_img_components,

View File

@ -14,7 +14,7 @@ from gerber.render import RenderSettings
from gerber.render.cairo_backend import GerberCairoContext from gerber.render.cairo_backend import GerberCairoContext
if len(sys.argv) < 3: if len(sys.argv) < 3:
print "Error! Please specify the gerber path, image filename and board side." print ("Error! Please specify the gerber path, image filename and board side.")
sys.exit(1) sys.exit(1)
gerberPath = sys.argv[1] gerberPath = sys.argv[1]
imageFileName = sys.argv[2] imageFileName = sys.argv[2]
@ -145,20 +145,20 @@ curTheme = jlcTheme
# choose layers # choose layers
if boardSide == 'top': if boardSide == 'top':
print "* Top Side:" print ("* Top Side:")
boardLayers = pcb.top_layers + pcb.drill_layers boardLayers = pcb.top_layers + pcb.drill_layers
isOutline = False isOutline = False
elif boardSide == 'bottom': elif boardSide == 'bottom':
print "* Bottom Side:" print ("* Bottom Side:")
boardLayers = pcb.bottom_layers + pcb.drill_layers boardLayers = pcb.bottom_layers + pcb.drill_layers
isOutline = False isOutline = False
elif boardSide == 'outline': elif boardSide == 'outline':
print "* Board Outline:" print ("* Board Outline:")
boardLayers = [pcb.outline_layer] + pcb.drill_layers boardLayers = [pcb.outline_layer] + pcb.drill_layers
curTheme = outlineTheme curTheme = outlineTheme
isOutline = True isOutline = True
else: else:
print "Error! Please specify the valid board side." print ("Error! Please specify the valid board side.")
sys.exit(2) sys.exit(2)
# render # render

View File

@ -1 +0,0 @@
node_modules

View File

@ -0,0 +1,93 @@
'''
ModernGL extension for storing mesh data
[andreika]: Modified to support vertex colors instead of texture coords
'''
import logging
import re
import struct
from pyrr import aabb
log = logging.getLogger('ModernGL.ext.obj')
RE_COMMENT = re.compile(r'#[^\n]*\n', flags=re.M)
RE_VERT = re.compile(r'^v\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)$')
RE_COLOR = re.compile(r'^vc\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)(?:\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?))?$')
RE_NORM = re.compile(r'^vn\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)\s+(-?\d+(?:\.\d+)?(?:[Ee]-?\d+)?)$')
RE_FACE = re.compile(r'^f\s+(\d+)(/(\d+)?(/(\d+))?)?\s+(\d+)(/(\d+)?(/(\d+))?)?\s+(\d+)(/(\d+)?(/(\d+))?)?$')
PACKER = 'lambda vx, vy, vz, cx, cy, cz, nx, ny, nz: struct.pack("%df", %s)'
def default_packer(vx, vy, vz, cx, cy, cz, nx, ny, nz):
return struct.pack('9f', vx, vy, vz, cx, cy, cz, nx, ny, nz)
def int_or_none(x):
return None if x is None else int(x)
def safe_float(x):
return 0.0 if x is None else float(x)
class Mesh:
def __init__(self, vert, color, norm, face):
self.vert = vert
self.color = color
self.norm = norm
self.face = face
# we need AABB to zoom in the model
self.aabb = aabb.create_zeros()
def pack(self, packer=default_packer) -> bytes:
'''
Args:
packer (str or lambda): The vertex attributes to pack.
Returns:
bytes: The packed vertex data.
Examples:
.. code-block:: python
import ModernGL
from ModernGL.ext import obj
model = obj.Obj.open('box.obj')
# default packer
data = model.pack()
# same as the default packer
data = model.pack('vx vy vz tx ty tz nx ny nz')
# pack vertices
data = model.pack('vx vy vz')
# pack vertices and texture coordinates (xy)
data = model.pack('vx vy vz tx ty')
# pack vertices and normals
data = model.pack('vx vy vz nx ny nz')
# pack vertices with padding
data = model.pack('vx vy vz 0.0')
'''
if isinstance(packer, str):
nodes = packer.split()
packer = eval(PACKER % (len(nodes), ', '.join(nodes)))
result = bytearray()
for v, t, n in self.face:
vx, vy, vz = self.vert[v - 1]
cx, cy, cz = self.color[t - 1] if t is not None else (0.0, 0.0, 0.0)
nx, ny, nz = self.norm[n - 1] if n is not None else (0.0, 0.0, 0.0)
result += packer(vx, vy, vz, cx, cy, cz, nx, ny, nz)
return bytes(result)

View File

@ -1,8 +0,0 @@
{
"dependencies": {
"fs": "0.0.1-security",
"pngjs": "^6.0.0",
"puppeteer": "^5.5.0",
"zlib": "^1.0.5"
}
}

View File

@ -1,79 +0,0 @@
/*
############################################################################################
# Hellen-One: A board rendering server script.
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
Node.js is required to run this script: https://nodejs.org/en/download/
Also please install PNGJS Node modules before running:
> npm install --prefix ./bin/render_vrml pngjs
*/
function getPixel(img, x, y) {
if (x < 0 || y < 0 || x >= img.width || y >= img.height)
return [0, 0, 0, 0];
var idx = (img.width * y + x) << 2;
return [ img.data[idx], img.data[idx + 1], img.data[idx + 2], img.data[idx + 3] ];
}
function createBoardImg(pcbImg, outlineImg, compImg, compImgOffset) {
var boardImg = new PNG({
width: Math.max(pcbImg.width, outlineImg.width, compImg.width),
height: Math.max(pcbImg.height, outlineImg.height, compImg.height) });
var pcbOffY = (pcbImg.height < outlineImg.height) ? -(outlineImg.height - pcbImg.height) : 0;
// Blit the pcbImg using the outlineImg mask and add compImg.
// We cannot use PNG.bitblt() for that
for (var y = 0; y < boardImg.height; y++) {
for (var x = 0; x < boardImg.width; x++) {
var bPixel = getPixel(pcbImg, x, y + pcbOffY);
var cPixel = getPixel(compImg, x + compImgOffset[0], y + compImgOffset[1]);
var a = parseFloat(cPixel[3]) / 255.0;
var na = 1.0 - a;
var idx = (boardImg.width * y + x) << 2;
boardImg.data[idx + 0] = na * bPixel[0] + a * cPixel[0];
boardImg.data[idx + 1] = na * bPixel[1] + a * cPixel[1];
boardImg.data[idx + 2] = na * bPixel[2] + a * cPixel[2];
boardImg.data[idx + 3] = na * getPixel(outlineImg, x, y)[0] + a * 255;
}
}
return boardImg;
}
try {
var PNG = require("pngjs").PNG;
// built-in modules
var fs = require("fs");
var args = process.argv.slice(2);
if (args.length < 4) {
console.log("* Error! Please specify correct arguments to run this script!");
process.exit(1)
}
// arguments
var pcbImgFile = args[0];
var outlineImgFile = args[1];
var compImgFile = args[2];
var boardImgFile = args[3];
var compImgOffset = args[4].split(",").map((x) => parseInt(x));
console.log("* Reading the pcb image...");
var pcbImg = PNG.sync.read(fs.readFileSync(pcbImgFile));
console.log("* Reading the components image with offset (" + compImgOffset[0] + "," + compImgOffset[1] + ")...");
var compImg = PNG.sync.read(fs.readFileSync(compImgFile));
console.log("* Reading the outline image...");
var outlineImg = PNG.sync.read(fs.readFileSync(outlineImgFile));
console.log("* Creating the final board image...");
var boardImg = createBoardImg(pcbImg, outlineImg, compImg, compImgOffset);
console.log("* Saving the board image...");
fs.writeFileSync(boardImgFile, PNG.sync.write(boardImg, { colorType: 6 }));
console.log("* Done! Exiting...");
} catch(e) {
if (e.message.indexOf("Cannot find module") !== -1) {
console.error("Error! `pngjs` library is not installed? Try running `npm install --prefix ./bin/render_vrml`.");
}
console.log(e);
process.exit(e.code);
}

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
############################################################################################
# Hellen-One: A board rendering server script.
# Python 3.5+ is required.
# Dependencies: Pillow
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
import os, sys
from PIL import Image
if (len(sys.argv) < 6):
print ("* Error! Please specify correct arguments to run this script!")
sys.exit(1)
pcbImgFile = sys.argv[1]
outlineImgFile = sys.argv[2]
compImgFile = sys.argv[3]
boardImgFile = sys.argv[4]
compImgOffset = [int(n) for n in sys.argv[5].split(",")]
class ImageObject:
width = 0
height = 0
data = []
def getPixel(img, x, y):
if (x < 0 or y < 0 or x >= img.width or y >= img.height):
return [0, 0, 0, 0]
return img.data[x, y]
def createBoardImg(pcbImg, outlineImg, compImg, compImgOffset):
width = max([pcbImg.width, outlineImg.width, compImg.width])
height = max([pcbImg.height, outlineImg.height, compImg.height])
boardImg = Image.new('RGBA', (width, height))
pcbOffY = -(outlineImg.height - pcbImg.height) if (pcbImg.height < outlineImg.height) else 0
# Blit the pcbImg using the outlineImg mask and add compImg.
# We cannot use PNG.bitblt() for that
for y in range(0, boardImg.height):
for x in range(0, boardImg.width):
bPixel = getPixel(pcbImg, x, y + pcbOffY)
cPixel = getPixel(compImg, x + compImgOffset[0], y + compImgOffset[1])
a = float(cPixel[3]) / 255.0
na = 1.0 - a
pr = int(na * bPixel[0] + a * cPixel[0])
pg = int(na * bPixel[1] + a * cPixel[1])
pb = int(na * bPixel[2] + a * cPixel[2])
pa = int(na * getPixel(outlineImg, x, y)[0] + a * 255)
boardImg.putpixel((x, y), (pr, pg, pb, pa))
return boardImg
def loadImage(fileName):
pimg = Image.open(fileName).convert("RGBA")
img = ImageObject()
img.data = pimg.load()
img.width = pimg.size[0]
img.height = pimg.size[1]
return img
print ("* Reading the pcb image...")
pcbImg = loadImage(pcbImgFile)
print ("* Reading the components image with offset (" + str(compImgOffset[0]) + "," + str(compImgOffset[1]) + ")...")
compImg = loadImage(compImgFile)
print ("* Reading the outline image...")
outlineImg = loadImage(outlineImgFile)
print ("* Creating the final board image...")
boardImg = createBoardImg(pcbImg, outlineImg, compImg, compImgOffset)
print ("* Saving the board image...")
boardImg.save(boardImgFile)
print ("* Done! Exiting...")

View File

@ -1,100 +0,0 @@
/*
############################################################################################
# Hellen-One: A 3D-component VRML rendering server script.
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
Node.js is required to run this script: https://nodejs.org/en/download/
Also please install Puppeteer and PNGJS Node modules before running:
> npm install --prefix ./bin/render_vrml puppeteer
> npm install --prefix ./bin/render_vrml pngjs
*/
try {
const puppeteer = require("puppeteer");
var PNG = require("pngjs").PNG;
// built-in modules
var fs = require("fs");
const zlib = require("zlib");
var args = process.argv.slice(2);
if (args.length < 3) {
console.log("* Error! Please specify correct arguments to run this script!");
process.exit(1)
}
// arguments
var vrmlFile = args[0];
var compImgFile = args[1];
var dpi = parseFloat(args[2]);
console.log("* Starting Puppeteer (" + vrmlFile + " dpi=" + dpi + ")...");
(async () => {
const browser = await puppeteer.launch({
timeout: 60000,
slowMo: 500,
headless: true,
args: ['--unlimited-storage', '--full-memory-crash-report']
});
const page = await browser.newPage();
var contentHtml = fs.readFileSync("bin/render_vrml/render_vrml.html", "utf8");
console.log("* Reading the input file...");
var vrmlData = fs.readFileSync(vrmlFile);
var vrmlDataType = "octet-stream";
if (vrmlFile.endsWith('.gz')) {
// we unzip it later, on the frontend side
vrmlDataType = "x-gzip";
}
var vrmlDataBase64 = vrmlData.toString("base64");
// injecting the data to the client script
contentHtml = contentHtml.replace("___SCREEN_DPI___", dpi);
contentHtml = contentHtml.replace("___VRML_DATA___", "data:application/" + vrmlDataType + ";base64," + vrmlDataBase64);
page.on("console", message => {
message.args().forEach(async (arg) => {
const val = await arg.jsonValue()
if (JSON.stringify(val) !== JSON.stringify({}))
console.log(`* Script: ` + val)
else {
const { type, subtype, description } = arg._remoteObject;
console.log(`* Script: ${type} ${subtype}:\n ${description}`)
}
})
})
.on("pageerror", ({ message }) => console.log(`* LOG_ERROR: ` + message))
.on("response", response => console.log(`* Loading: ` + `${response.status()} ${response.url()}`))
.on("requestfailed", request => console.log(`* Loading Failed: ` + `${request.failure() ? request.failure().errorText : "?"} ${request.url()}`));
process.on('unhandledRejection', (reason, p) => {
console.error('Unhandled Rejection at: Promise', p, 'reason:', reason);
browser.close();
});
await page.setDefaultNavigationTimeout(90000); // 90 seconds
console.log("* Executing the script (content size is " + contentHtml.length + " bytes)...");
await page.setContent(contentHtml, { waitUntil: 'domcontentloaded' });
console.log("* Waiting for completion...");
const watchDog = page.waitForFunction("document.done", {timeout: 180000}); // 180 seconds
await watchDog;
var screenWidth = await page.evaluate(() => document.compImgWidth);
var screenHeight = await page.evaluate(() => document.compImgHeight);
console.log("* Saving the screenshot (" + screenWidth + "x" + screenHeight + ")");
await page.setViewport({width: screenWidth, height: screenHeight, deviceScaleFactor: 1, });
await page.screenshot({path: compImgFile, omitBackground: true});
await page.close();
await browser.close();
console.log("* Done! Exiting...");
})();
} catch(e) {
if (e.message.indexOf("Cannot find module") !== -1) {
console.error("Error! `Puppeteer` or `pngjs` library is not installed? Try running `npm install --prefix ./bin/render_vrml`.");
}
console.log(e);
process.exit(e.code);
}

View File

@ -0,0 +1,152 @@
#!/usr/bin/env python
############################################################################################
# Hellen-One: A 3D-component VRML rendering server script.
# Python 3.5+ is required.
# Dependencies: ModernGL, Pillow, Pyrr, NumPy, PyVRML97, Gzip
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
import os, sys
import numpy as np
from pyrr import Matrix33,Matrix44,Vector3,Vector4,aabb
from vrml.vrml97 import parser,basenodes
from vrml import *
import moderngl as ModernGL
from moderngl_mesh import Mesh
from PIL import Image
import gzip
if (len(sys.argv) < 3):
print ("Error! Please specify the VRML+image file names and DPI.")
sys.exit(1)
fileName = sys.argv[1]
outFileName = sys.argv[2]
dpi = float(sys.argv[3])
curLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
# uses ModernGL (OpenGL-based renderer with HW acceleration)
def render(model, outFileName, dpi):
vertex_data = model.pack('vx vy vz cx cy cz nx ny nz')
# Context creation
ctx = ModernGL.create_standalone_context()
# Shaders
vertex_shader_source = open(os.path.join(curLocation, 'shader.vert')).read()
fragment_shader_source = open(os.path.join(curLocation, 'shader.frag')).read()
prog = ctx.program(vertex_shader = vertex_shader_source, fragment_shader = fragment_shader_source)
# Matrices and Uniforms
perspective = Matrix44.orthogonal_projection(model.aabb[0][0], model.aabb[1][0], model.aabb[0][1], model.aabb[1][1], 0.1, 1000.0) # left,right,top,bottom,near,far
lookat = Matrix44.look_at(
(0,0,300),
(0,0,0),
(0.0, 1.0, 0.0),
)
mvp = perspective * lookat
prog['LightDir'].value = (0.0, 0.0, -1.0)
prog['AmbientColor'].value = (1.0, 1.0, 1.0, 0.25)
prog['Mvp'].write(mvp.astype('f4').tobytes())
# Vertex Buffer and Vertex Array
vbo = ctx.buffer(vertex_data)
vao = ctx.simple_vertex_array(prog, vbo, * ['in_vert', 'in_color', 'in_norm'])
(width, height) = ((model.aabb[1][0:2] - model.aabb[0][0:2]) * dpi / 25.4 + 0.5).astype(int)
print ("* Image size = " + str(width) + "x" + str(height))
# Framebuffer
fbo = ctx.framebuffer(ctx.renderbuffer((width, height)), ctx.depth_renderbuffer((width, height)),)
# Rendering
fbo.use()
ctx.enable(ModernGL.DEPTH_TEST)
ctx.clear(0.0, 0.0, 0.0, 0.0)
vao.render()
# Save the image using Pillow
print ("* Saving to " + str(outFileName) + "...")
data = fbo.read(components=4, alignment=1)
img = Image.frombytes('RGBA', fbo.size, data, 'raw', 'RGBA', 0, -1)
img.save(outFileName)
def addFaces(model, faces, mat, tm):
offs = len(model.vert)
# create an inverse transform matrix for normals
tnm = Matrix33(tm)
tnm = tnm.inverse.T
# add vertices
for i in range(0, len(faces.coord.point)):
# position
x = float(faces.coord.point[i][0])
y = float(faces.coord.point[i][1])
z = float(faces.coord.point[i][2])
v = Vector4([x, y, z, 1.0])
tv = tm * v
# get normal (if present)
try:
(nx,ny,nz) = (faces.normal.vector[i][0:3])
nv = Vector3([nx, ny, nz])
except AttributeError:
nv = Vector3([0,0,-1]) # default normal
tnv = tnm * nv
tnv.normalize()
model.vert.append((tv.x, tv.y, tv.z))
model.norm.append((tnv.x, tnv.y, tnv.z))
model.color.append((mat.diffuseColor[0], mat.diffuseColor[1], mat.diffuseColor[2]))
model.aabb = aabb.add_points(model.aabb, [tv.xyz])
# add indices
numFaces = int(len(faces.coordIndex) / 4)
for i in range(0, numFaces):
for ci in range(0, 3):
idx = faces.coordIndex[i * 4 + ci]
# indices are 1-based
ind = int(idx + offs) + 1
model.face.append((ind, ind, ind))
def processChildren(model, ch, tm):
# iterate through all nodes
for i,node in enumerate(ch):
if (type(node) is basenodes.Group):
processChildren(model, node.children, tm)
continue
if (type(node) is basenodes.Transform):
transform = Matrix44(node.localMatrices().data[0].tolist()) if (node.localMatrices().data[0] is not None) else Matrix44.identity()
# apply transform
combinedTransform = tm * transform
processChildren(model, node.children, combinedTransform)
continue
if (type(node) is basenodes.Shape and type(node.geometry) is basenodes.IndexedFaceSet):
addFaces(model, node.geometry, node.appearance.material, tm)
############################################################################################
print ("* Reading " + fileName + "...")
if fileName.endswith('.gz'):
print ("* Decompressing GZip...")
data = gzip.open(fileName, "rt").read()
else:
data = open(fileName).read()
print ("* Parsing the VRML data...")
parser = vrml97.parser.buildParser()
success, tags, next = parser.parse(data)
if (not success):
print("VRML Parsing error: parsed %s characters of %s"%(next, len(data)))
sys.exit(1)
print ("* Processing the data...")
model = Mesh([], [], [], [])
processChildren(model, tags[1].children, Matrix44.identity())
print ("* Num.Vertices = " + str(len(model.vert)) + " Num.Indices = " + str(len(model.face)))
print ("* AABB = " + str(model.aabb))
print ("* Rendering...")
render(model, outFileName, dpi)
print ("* Done!")

View File

@ -1,106 +0,0 @@
<!--
############################################################################################
# Hellen-One: A 3D-component VRML rendering client script.
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
--><html><head>
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
background-color: transparent;
}
</style></head><body><script type="module">
// these are replaced with the real data by the parent script
var dpi = ___SCREEN_DPI___;
var vrmlData = "___VRML_DATA___";
// the distance from the camera to the board doesn't really matter because of the otho-projection
var dist = 100;
var minDist = 0.001;
// the far clipping plane is intentionally set to be less than the dist - to clip the board surface!
var boardSurfaceClippingThreshold = 0.001;
var gzHeader = "data:application/x-gzip;base64,";
// use the official Three.js public distribution
import * as THREE from 'https://unpkg.com/three@0.119.0/build/three.module.js';
import { VRMLLoader } from 'https://unpkg.com/three@0.119.0/examples/jsm/loaders/VRMLLoader.js';
import * as fflate from 'https://unpkg.com/three@0.128.0/examples/jsm/libs/fflate.module.js';
console.log('Starting...');
// this var is checked by the parent Node.js script
document.done = false;
// these are used in the parent caller script
document.compImgWidth = 0;
document.compImgHeight = 0;
var renderer;
function load() {
return new Promise((resolve, reject) => {
if (vrmlData.substring(0, gzHeader.length) === gzHeader) {
console.log('Decompressing GZip...');
vrmlData = vrmlData.substring(gzHeader.length);
var vrmlDataBytes = fflate.strToU8(atob(vrmlData), true);
var vrmlDataUncompressed = fflate.gunzipSync(vrmlDataBytes);
var vrmlDataUncompressedB64 = btoa(fflate.strFromU8(vrmlDataUncompressed));
vrmlData = "data:application/octet-stream;base64," + vrmlDataUncompressedB64;
console.log('Decompressed ' + vrmlDataUncompressed.length + ' bytes...');
}
console.log('Loading VRML...');
var loader = new VRMLLoader();
loader.load(vrmlData, function (object) {
console.log('Scene and camera setup...');
var box = new THREE.Box3().setFromObject(object);
var centerPos = new THREE.Vector3();
box.getCenter(centerPos);
var scene = new THREE.Scene();
scene.add(object);
// get the board size
var boxSize = new THREE.Vector3();
box.getSize(boxSize);
// the bounding size is in mm (metric), and DPI is 'imperial'
// we need width/height in pixels (rounded)
document.compImgWidth = parseInt(dpi * boxSize.x / 25.4 + 0.5);
document.compImgHeight = parseInt(dpi * boxSize.y / 25.4 + 0.5);
// setup the camera
var w2 = boxSize.x / 2;
var h2 = boxSize.y / 2;
var camera = new THREE.OrthographicCamera(-w2, w2, h2, -h2, minDist, dist - boardSurfaceClippingThreshold);
camera.position.set(centerPos.x, centerPos.y, dist);
scene.add(camera);
console.log('Rendering a static frame...');
renderer.setSize(document.compImgWidth, document.compImgHeight);
renderer.render(scene, camera);
// notify async consumers
resolve();
document.done = true;
});
});
}
async function run() {
console.log('Init renderer...');
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true});
renderer.setClearColor(0x000000, 0);
document.body.appendChild(renderer.domElement);
await load();
console.log('Exiting script...');
}
run();
</script></body></html>

View File

@ -0,0 +1,32 @@
#version 330
uniform vec4 AmbientColor;
uniform vec3 LightDir;
in vec3 v_vert;
in vec3 v_norm;
in vec3 v_color;
out vec4 f_color;
void main() {
// clip negative fragments (below the board surface level)
if (v_vert.z < 0.1) {
discard;
return;
}
// we use abs() to make normals compatible with both CW and CCW faces
vec3 n = normalize(abs(v_norm));
// calc luminosity
float lum = dot(n, LightDir);
lum = acos(lum) / 3.14159265;
lum = clamp(lum, 0.0, 1.0);
lum = lum * lum;
lum = smoothstep(0.0, 1.0, lum);
// modulate
vec3 lcolor = v_color * lum;
// add ambient color
vec3 color = lcolor * (1.0 - AmbientColor.a) + AmbientColor.rgb * AmbientColor.a;
f_color = vec4(color, 1.0);
}

View File

@ -0,0 +1,18 @@
#version 330
uniform mat4 Mvp;
in vec3 in_vert;
in vec3 in_norm;
in vec3 in_color;
out vec3 v_vert;
out vec3 v_norm;
out vec3 v_color;
void main() {
v_vert = in_vert;
v_norm = in_norm;
v_color = in_color;
gl_Position = Mvp * vec4(v_vert, 1.0);
}