From 3db821481917823e1efa2c4fd763841c881d0d8a Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 19 May 2021 13:07:18 +0300 Subject: [PATCH] https://github.com/andreika-git/hellen-one/issues/10 --- bin/create_3d_components.py | 43 +++++++++++++++++++--------- bin/process_board.py | 2 -- bin/render_vrml/render_board.js | 2 +- bin/render_vrml/render_components.js | 41 +++++++++++++++++--------- bin/render_vrml/render_vrml.html | 21 ++++++++++++-- 5 files changed, 76 insertions(+), 33 deletions(-) diff --git a/bin/create_3d_components.py b/bin/create_3d_components.py index e644357..f8b30e8 100644 --- a/bin/create_3d_components.py +++ b/bin/create_3d_components.py @@ -4,14 +4,14 @@ # (c) andreika ############################################################################################ -import os, sys, re +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." + print ("Error! Please specify the place+board files and vrml filename.") sys.exit(1) mergePlaceFile = sys.argv[1] mergeBoardFile = sys.argv[2] @@ -32,42 +32,57 @@ with open(mergePlaceFile, 'rb') as fmp: m = {"name": name, "x": m[1], "y": m[2], "rot": rot, "path": config[name]["Prefix"] } fragments.append(m) -print "* Starting merge of " + str(len(fragments)) + " board fragments..." +print ("* Starting merge of " + str(len(fragments)) + " board fragments...") outf = gzip.open(fileOutName, 'wb') outf.write("#VRML V2.0 utf8\n") pat_hdr = re.compile('^#VRML.*') pat_idx = re.compile(r'(DEF|USE)\s+(Shape|_)(\w)') -pat_trans = re.compile(r'(translation)\s+([+\-0-9\.]+)\s+([+\-0-9\.]+)') - -def trans_repl(m): - x = float(m.group(2)) - y = float(m.group(3)) - return m.group(1) + " " + str(x + off_x_mm) + " " + str(y + off_y_mm) +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 - # todo: apply module rotations rot = float(frag["rot"]) fileName = frag["path"] + "-vrml.wrl" - print "* Adding " + frag["name"] + " (" + fileName + ") at (" + str(off_x_mm) + "," + str(off_y_mm) + "), rot=" + str(rot) + "..." + z_offset = 0 + 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) + "...") with open(fileName, 'rb') 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 - fragId = str(fId).zfill(2) + # add global transformation for the module + if not was_global_transform: + # 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!") + # the board is 1.6 mm thick? + # todo: this is a 'hack' + z_offset = -1.6 + outf.write("DEF TX" + frag["name"] + " 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 1.0 1.0\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) - line = pat_trans.sub(trans_repl, line) outf.write(line + "\n") f.close() + if was_global_transform: + outf.write("] }\n") fId = fId + 1 outf.close() -print "Done!" +print ("Done!") diff --git a/bin/process_board.py b/bin/process_board.py index b180587..75d630c 100644 --- a/bin/process_board.py +++ b/bin/process_board.py @@ -341,7 +341,6 @@ subprocess.call([sys.executable, "bin/create_3d_components.py", board_cfg_path, board_misc_path_name + "-3D.wrl.gz"]) -""" print ("Rendering a 3D-model of the board components...") subprocess.call([node_bin, "bin/render_vrml/render_components.js", board_misc_path_name + "-3D.wrl.gz", @@ -369,7 +368,6 @@ subprocess.call([sys.executable, "bin/gen_iBOM.py", "./ibom-data", rotations, board_path_name + "-ibom.html"]) -""" print ("Cleaning up...") delete_file(board_cfg_path) diff --git a/bin/render_vrml/render_board.js b/bin/render_vrml/render_board.js index d848cb0..36433db 100644 --- a/bin/render_vrml/render_board.js +++ b/bin/render_vrml/render_board.js @@ -20,7 +20,7 @@ function createBoardImg(pcbImg, outlineImg, compImg) { 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 < boardImg.height) ? -(boardImg.height - pcbImg.height) : 0; + 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++) { diff --git a/bin/render_vrml/render_components.js b/bin/render_vrml/render_components.js index 30b2f01..37c3203 100644 --- a/bin/render_vrml/render_components.js +++ b/bin/render_vrml/render_components.js @@ -27,24 +27,31 @@ try { var compImgFile = args[1]; var dpi = parseFloat(args[2]); - console.log("* Starting Puppeteer (" + vrmlFile + " dpi=" + dpi + "..."); + console.log("* Starting Puppeteer (" + vrmlFile + " dpi=" + dpi + ")..."); (async () => { - const browser = await puppeteer.launch(); + 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); + console.log("* Reading the input file..."); + var vrmlData = fs.readFileSync(vrmlFile); + var vrmlDataType = "octet-stream"; if (vrmlFile.endsWith('.gz')) { - vrmlData = zlib.gunzipSync(vrmlData); + // we unzip it later, on the frontend side + vrmlDataType = "x-gzip"; } - var vrmlDataBase64 = vrmlData.toString("base64"); + 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/gzip;base64," + vrmlDataBase64); + // 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) => { @@ -59,12 +66,19 @@ try { }) .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().errorText} ${request.url()}`)); + .on("requestfailed", request => console.log(`* Loading Failed: ` + `${request.failure() ? request.failure().errorText : "?"} ${request.url()}`)); - console.log("* Executing the script..."); - await page.setContent(contentHtml); + 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"); + const watchDog = page.waitForFunction("document.done", {timeout: 180000}); // 180 seconds await watchDog; var screenWidth = await page.evaluate(() => document.compImgWidth); @@ -72,6 +86,7 @@ try { 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..."); })(); diff --git a/bin/render_vrml/render_vrml.html b/bin/render_vrml/render_vrml.html index cfd7e1b..0c7a784 100644 --- a/bin/render_vrml/render_vrml.html +++ b/bin/render_vrml/render_vrml.html @@ -21,12 +21,16 @@ 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.0001; +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 @@ -39,6 +43,18 @@ 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...'); @@ -78,11 +94,10 @@ function load() { async function run() { console.log('Init renderer...'); - renderer = new THREE.WebGLRenderer({antialias: true, alpha: true, preserveDrawingBuffer: true}); + renderer = new THREE.WebGLRenderer({antialias: true, alpha: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true}); renderer.setClearColor(0x000000, 0); document.body.appendChild(renderer.domElement); - console.log('Loading VRML...'); await load(); console.log('Exiting script...'); }