initial commit
This commit is contained in:
commit
fe1c0750a2
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const puppeteer_1 = __importDefault(require("puppeteer"));
|
||||||
|
const argparse_1 = require("argparse");
|
||||||
|
const path_1 = require("path");
|
||||||
|
const promises_1 = require("fs/promises");
|
||||||
|
const URL = 'https://mattmcw.github.io/plotter-vision/';
|
||||||
|
const DOWNLOADS = './downloads';
|
||||||
|
const WIDTH_DEFAULT = 1920;
|
||||||
|
const HEIGHT_DEFAULT = 1080;
|
||||||
|
let PROCESSING = false;
|
||||||
|
let PROCESSING_MONITOR = false;
|
||||||
|
let WAITING = false;
|
||||||
|
async function delay(ms) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
return setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function compare(before, after) {
|
||||||
|
let downloaded = null;
|
||||||
|
for (let file of after) {
|
||||||
|
if (before.indexOf(file) === -1) {
|
||||||
|
console.log(`Downloaded ${(0, path_1.join)(DOWNLOADS, file)}`);
|
||||||
|
downloaded = (0, path_1.resolve)((0, path_1.join)(DOWNLOADS, file));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return downloaded;
|
||||||
|
}
|
||||||
|
async function dir(path) {
|
||||||
|
const files = await (0, promises_1.readdir)(path);
|
||||||
|
return files.filter((el) => { return (0, path_1.extname)(el) === '.svg'; });
|
||||||
|
}
|
||||||
|
async function clearDownloads() {
|
||||||
|
const files = await dir(DOWNLOADS);
|
||||||
|
for (let file of files) {
|
||||||
|
await (0, promises_1.unlink)((0, path_1.resolve)((0, path_1.join)(DOWNLOADS, file)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function cli() {
|
||||||
|
const parser = new argparse_1.ArgumentParser({ description: 'STL to SVG', usage: '%(prog)s <options> [input] [output]' });
|
||||||
|
parser.add_argument('input', { type: 'str', help: 'Input STL to render' });
|
||||||
|
parser.add_argument('output', { type: 'str', help: 'Output SVG to write' });
|
||||||
|
parser.add_argument('--width', '-W', { type: 'int', default: WIDTH_DEFAULT, help: `Width of window to render STL (default: ${WIDTH_DEFAULT})` });
|
||||||
|
parser.add_argument('--height', '-H', { type: 'int', default: HEIGHT_DEFAULT, help: `Height of window to render STL (default: ${HEIGHT_DEFAULT})` });
|
||||||
|
parser.add_argument('--load-delay', '-l', { type: 'int', default: 0, help: 'Number of milliseconds to wait after loading (default: 0)' });
|
||||||
|
parser.add_argument('--save-delay', '-s', { type: 'int', default: 0, help: 'Number of milliseconds to wait before saving (default: 0)' });
|
||||||
|
parser.add_argument('--theta', '-t', { type: 'float', default: null, help: 'Theta of the camera' });
|
||||||
|
parser.add_argument('--psi', '-p', { type: 'float', default: null, help: 'Psi of the camera' });
|
||||||
|
parser.add_argument('--radius', '-r', { type: 'float', default: null, help: 'Radius of the camera' });
|
||||||
|
parser.add_argument('--center', '-c', { type: 'str', default: null, help: 'LookAt center' });
|
||||||
|
parser.add_argument('--no-display', '-n', { action: 'store_true', default: true, help: 'No display' });
|
||||||
|
return parser.parse_args();
|
||||||
|
}
|
||||||
|
async function download(page, dest, save) {
|
||||||
|
let before;
|
||||||
|
let after;
|
||||||
|
let downloaded = null;
|
||||||
|
before = await dir(DOWNLOADS);
|
||||||
|
//console.dir(before)
|
||||||
|
await page.click('#fileDownload');
|
||||||
|
console.log(`Clicked download`);
|
||||||
|
await delay(100);
|
||||||
|
if (save > 0) {
|
||||||
|
console.log(`Delaying for: ${save}ms`);
|
||||||
|
await delay(save);
|
||||||
|
}
|
||||||
|
while (downloaded === null) {
|
||||||
|
await delay(1);
|
||||||
|
after = await dir(DOWNLOADS);
|
||||||
|
downloaded = compare(before, after);
|
||||||
|
}
|
||||||
|
if (downloaded === null) {
|
||||||
|
console.warn(`Cannot move download file to destination: ${dest}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await (0, promises_1.copyFile)(downloaded, dest);
|
||||||
|
await (0, promises_1.unlink)(downloaded);
|
||||||
|
console.dir(`${downloaded} => ${dest}`);
|
||||||
|
}
|
||||||
|
function startProcessing() {
|
||||||
|
PROCESSING = true;
|
||||||
|
PROCESSING_MONITOR = true;
|
||||||
|
}
|
||||||
|
async function waitForProcessing() {
|
||||||
|
while (PROCESSING) {
|
||||||
|
await delay(401);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
async function main() {
|
||||||
|
startProcessing();
|
||||||
|
const args = cli();
|
||||||
|
console.dir(args);
|
||||||
|
const config = {
|
||||||
|
headless: !args.no_display,
|
||||||
|
args: [`--window-size=${args.width},${args.height}`]
|
||||||
|
};
|
||||||
|
await clearDownloads();
|
||||||
|
const browser = await puppeteer_1.default.launch(config);
|
||||||
|
const page = await browser.newPage();
|
||||||
|
let lookat = [0, 0, 0];
|
||||||
|
page.on('console', (message) => {
|
||||||
|
const text = message.text();
|
||||||
|
const type = message.type().substr(0, 3).toUpperCase();
|
||||||
|
if (type === 'LOG' && text.trim() === 'hidden processing') {
|
||||||
|
PROCESSING = true;
|
||||||
|
PROCESSING_MONITOR = true;
|
||||||
|
}
|
||||||
|
else if (type === 'LOG' && text.indexOf('hidden processing ') !== -1 && text.indexOf(' segments ') !== -1) {
|
||||||
|
PROCESSING_MONITOR = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!PROCESSING_MONITOR && PROCESSING) {
|
||||||
|
PROCESSING = false;
|
||||||
|
console.log('PROCESSING COMPLETE');
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
if (!text.startsWith('filtered ')) {
|
||||||
|
console.log(`${type} ${text}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const client = await page.target().createCDPSession();
|
||||||
|
await client.send('Page.setDownloadBehavior', {
|
||||||
|
behavior: 'allow',
|
||||||
|
downloadPath: DOWNLOADS,
|
||||||
|
});
|
||||||
|
await page.setViewport({ width: args.width, height: args.height });
|
||||||
|
await page.goto(URL, { waitUntil: 'networkidle2' });
|
||||||
|
await delay(100);
|
||||||
|
console.log(`Loading: ${args.input}`);
|
||||||
|
startProcessing();
|
||||||
|
const [fileChooser] = await Promise.all([
|
||||||
|
page.waitForFileChooser(),
|
||||||
|
page.click('#loadFileXml')
|
||||||
|
]);
|
||||||
|
await fileChooser.accept([args.input]);
|
||||||
|
startProcessing();
|
||||||
|
await waitForProcessing();
|
||||||
|
if (args.psi !== null || args.theta !== null) {
|
||||||
|
console.log(`Adjusting the camera rotation`);
|
||||||
|
startProcessing();
|
||||||
|
await page.evaluate(`cameraView(${args.theta === null ? 0 : args.theta}, ${args.psi === null ? 0 : args.psi});`);
|
||||||
|
}
|
||||||
|
if (args.radius !== null) {
|
||||||
|
console.log(`Adjusting the camera distance`);
|
||||||
|
startProcessing();
|
||||||
|
await page.evaluate(`camera_radius = ${args.radius}; reproject = true; vz = 0.00001;`); //vz = 0.00001;
|
||||||
|
}
|
||||||
|
if (args.center !== null) {
|
||||||
|
lookat = args.center.split(',').map((el) => { return parseFloat(el); });
|
||||||
|
await page.evaluate(`camera.lookat.x = ${lookat[0]};
|
||||||
|
camera.lookat.y = ${lookat[1]};
|
||||||
|
camera.lookat.z = ${lookat[2]};
|
||||||
|
vz = 0.00001;`);
|
||||||
|
}
|
||||||
|
if (args.load_delay > 0) {
|
||||||
|
console.log(`Delaying for: ${args.load_delay}ms`);
|
||||||
|
await delay(args.load_delay);
|
||||||
|
}
|
||||||
|
await waitForProcessing();
|
||||||
|
await download(page, args.output, args.save_delay);
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "plotter-vision-auto",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"compile": "./node_modules/.bin/tsc -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/argparse": "^2.0.17",
|
||||||
|
"@types/node": "^22.13.5",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1",
|
||||||
|
"puppeteer": "^24.2.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
import type { LaunchOptions, Page } from 'puppeteer';
|
||||||
|
import { ArgumentParser } from 'argparse';
|
||||||
|
import { join, resolve, extname } from 'path';
|
||||||
|
import { readdir, copyFile, unlink } from 'fs/promises';
|
||||||
|
|
||||||
|
const URL : string = 'https://mattmcw.github.io/plotter-vision/';
|
||||||
|
const DOWNLOADS : string = './downloads';
|
||||||
|
const WIDTH_DEFAULT : number = 1920;
|
||||||
|
const HEIGHT_DEFAULT : number = 1080;
|
||||||
|
|
||||||
|
let PROCESSING : boolean = false;
|
||||||
|
let PROCESSING_MONITOR : boolean = false;
|
||||||
|
let WAITING : boolean = false;
|
||||||
|
|
||||||
|
async function delay (ms : number) {
|
||||||
|
return new Promise((resolve : Function, reject : Function) => {
|
||||||
|
return setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compare (before : string[], after : string[]) : string {
|
||||||
|
let downloaded : string = null;
|
||||||
|
for (let file of after) {
|
||||||
|
if (before.indexOf(file) === -1) {
|
||||||
|
console.log(`Downloaded ${join(DOWNLOADS, file)}`);
|
||||||
|
downloaded = resolve(join(DOWNLOADS, file));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return downloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dir (path : string) : Promise<string[]> {
|
||||||
|
const files : string[] = await readdir(path);
|
||||||
|
return files.filter((el : string) => { return extname(el) === '.svg'; });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearDownloads () {
|
||||||
|
const files : string[] = await dir(DOWNLOADS);
|
||||||
|
for (let file of files) {
|
||||||
|
await unlink(resolve(join(DOWNLOADS, file)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cli () : any {
|
||||||
|
const parser : ArgumentParser = new ArgumentParser({ description: 'STL to SVG', usage : '%(prog)s <options> [input] [output]' });
|
||||||
|
parser.add_argument('input', { type : 'str', help : 'Input STL to render' });
|
||||||
|
parser.add_argument('output', { type : 'str', help : 'Output SVG to write' });
|
||||||
|
|
||||||
|
parser.add_argument('--width', '-W', { type : 'int', default : WIDTH_DEFAULT, help : `Width of window to render STL (default: ${WIDTH_DEFAULT})` });
|
||||||
|
parser.add_argument('--height', '-H', { type : 'int', default : HEIGHT_DEFAULT, help : `Height of window to render STL (default: ${HEIGHT_DEFAULT})` });
|
||||||
|
|
||||||
|
|
||||||
|
parser.add_argument('--load-delay', '-l', { type : 'int', default : 0, help : 'Number of milliseconds to wait after loading (default: 0)' });
|
||||||
|
parser.add_argument('--save-delay', '-s', { type : 'int', default : 0, help : 'Number of milliseconds to wait before saving (default: 0)' });
|
||||||
|
|
||||||
|
parser.add_argument('--theta', '-t', { type : 'float', default : null, help : 'Theta of the camera' });
|
||||||
|
parser.add_argument('--psi', '-p', { type : 'float', default : null, help : 'Psi of the camera' });
|
||||||
|
parser.add_argument('--radius', '-r', { type : 'float', default : null, help : 'Radius of the camera' });
|
||||||
|
parser.add_argument('--center', '-c', { type : 'str', default : null, help : 'LookAt center' });
|
||||||
|
|
||||||
|
parser.add_argument('--no-display', '-n', { action : 'store_true', default : true, help : 'No display' });
|
||||||
|
|
||||||
|
return parser.parse_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function download (page : Page, dest : string, save : number) {
|
||||||
|
let before : string[];
|
||||||
|
let after : string[];
|
||||||
|
let downloaded : string = null;
|
||||||
|
before = await dir(DOWNLOADS);
|
||||||
|
//console.dir(before)
|
||||||
|
|
||||||
|
await page.click('#fileDownload');
|
||||||
|
console.log(`Clicked download`);
|
||||||
|
await delay(100);
|
||||||
|
if (save > 0) {
|
||||||
|
console.log(`Delaying for: ${save}ms`);
|
||||||
|
await delay(save);
|
||||||
|
}
|
||||||
|
while (downloaded === null) {
|
||||||
|
await delay(1);
|
||||||
|
after = await dir(DOWNLOADS);
|
||||||
|
downloaded = compare(before, after);
|
||||||
|
}
|
||||||
|
if (downloaded === null) {
|
||||||
|
console.warn(`Cannot move download file to destination: ${dest}`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await copyFile(downloaded, dest);
|
||||||
|
await unlink(downloaded);
|
||||||
|
console.dir(`${downloaded} => ${dest}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startProcessing () {
|
||||||
|
PROCESSING = true;
|
||||||
|
PROCESSING_MONITOR = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForProcessing () : Promise<boolean> {
|
||||||
|
while (PROCESSING) {
|
||||||
|
await delay(401);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
startProcessing();
|
||||||
|
const args : any = cli();
|
||||||
|
console.dir(args)
|
||||||
|
const config : LaunchOptions = {
|
||||||
|
headless : !args.no_display,
|
||||||
|
args: [`--window-size=${args.width},${args.height}`]
|
||||||
|
};
|
||||||
|
await clearDownloads();
|
||||||
|
const browser = await puppeteer.launch(config);
|
||||||
|
const page = await browser.newPage();
|
||||||
|
let lookat : number[] = [0, 0, 0];
|
||||||
|
page.on('console', (message : any) => {
|
||||||
|
const text : string = message.text();
|
||||||
|
const type : string = message.type().substr(0, 3).toUpperCase();
|
||||||
|
if (type === 'LOG' && text.trim() === 'hidden processing') {
|
||||||
|
PROCESSING = true;
|
||||||
|
PROCESSING_MONITOR = true;
|
||||||
|
} else if (type === 'LOG' && text.indexOf('hidden processing ') !== -1 && text.indexOf(' segments ') !== -1) {
|
||||||
|
PROCESSING_MONITOR = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!PROCESSING_MONITOR && PROCESSING) {
|
||||||
|
PROCESSING = false;
|
||||||
|
console.log('PROCESSING COMPLETE');
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!text.startsWith('filtered ')) {
|
||||||
|
console.log(`${type} ${text}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const client = await page.target().createCDPSession();
|
||||||
|
await client.send('Page.setDownloadBehavior', {
|
||||||
|
behavior: 'allow',
|
||||||
|
downloadPath: DOWNLOADS,
|
||||||
|
})
|
||||||
|
await page.setViewport({ width: args.width, height: args.height });
|
||||||
|
await page.goto(URL, { waitUntil: 'networkidle2' });
|
||||||
|
await delay(100);
|
||||||
|
|
||||||
|
console.log(`Loading: ${args.input}`);
|
||||||
|
startProcessing();
|
||||||
|
const [fileChooser] = await Promise.all([
|
||||||
|
page.waitForFileChooser(),
|
||||||
|
page.click('#loadFileXml')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await fileChooser.accept([ args.input ]);
|
||||||
|
startProcessing();
|
||||||
|
|
||||||
|
await waitForProcessing();
|
||||||
|
|
||||||
|
if (args.psi !== null || args.theta !== null) {
|
||||||
|
console.log(`Adjusting the camera rotation`);
|
||||||
|
startProcessing();
|
||||||
|
await page.evaluate(`cameraView(${args.theta === null ? 0 : args.theta}, ${args.psi === null ? 0 : args.psi});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.radius !== null) {
|
||||||
|
console.log(`Adjusting the camera distance`);
|
||||||
|
startProcessing();
|
||||||
|
await page.evaluate(`camera_radius = ${args.radius}; reproject = true; vz = 0.00001;`); //vz = 0.00001;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.center !== null) {
|
||||||
|
lookat = args.center.split(',').map((el : string) => { return parseFloat(el); });
|
||||||
|
await page.evaluate(`camera.lookat.x = ${lookat[0]};
|
||||||
|
camera.lookat.y = ${lookat[1]};
|
||||||
|
camera.lookat.z = ${lookat[2]};
|
||||||
|
vz = 0.00001;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.load_delay > 0) {
|
||||||
|
console.log(`Delaying for: ${args.load_delay}ms`);
|
||||||
|
await delay(args.load_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
await waitForProcessing();
|
||||||
|
|
||||||
|
await download(page, args.output, args.save_delay);
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "ES2020",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments" : false,
|
||||||
|
"baseUrl" : "dist",
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"rootDir" : "./src/",
|
||||||
|
"paths" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude" : [
|
||||||
|
"./dist"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue