@kitware/vtk.js
Version:
Visualization Toolkit for the Web
232 lines (224 loc) • 6.9 kB
JavaScript
import { m as macro } from '../../macros2.js';
import BinaryHelper from '../Core/BinaryHelper.js';
import DataAccessHelper from '../Core/DataAccessHelper.js';
import vtkPolyData from '../../Common/DataModel/PolyData.js';
import vtkPoints from '../../Common/Core/Points.js';
import vtkCellArray from '../../Common/Core/CellArray.js';
import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + gz
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip
// ----------------------------------------------------------------------------
// vtkGCodeReader methods
// ----------------------------------------------------------------------------
function vtkGCodeReader(publicAPI, model) {
const state = {
currentPosition: {
x: 0,
y: 0,
z: 0
},
offset: {
x: 0,
y: 0,
z: 0
},
currentLayer: 0,
layers: new Map(),
// Map to store layer data
isAbsolute: true,
// G90 is default
isMetric: true,
// G21 is default
lastZ: 0 // Track Z changes for layer detection
};
model.classHierarchy.push('vtkGCodeReader');
if (!model.dataAccessHelper) {
model.dataAccessHelper = DataAccessHelper.get('http');
}
function fetchData(url, option = {}) {
const {
compression,
progressCallback
} = model;
if (option.binary) {
return model.dataAccessHelper.fetchBinary(url, {
compression,
progressCallback
});
}
return model.dataAccessHelper.fetchText(publicAPI, url, {
compression,
progressCallback
});
}
function detectLayerChange(newZ) {
if (Math.abs(newZ - state.lastZ) > 0.001) {
state.currentLayer++;
state.lastZ = newZ;
return true;
}
return false;
}
function initializeLayer() {
if (!state.layers.has(state.currentLayer)) {
const points = vtkPoints.newInstance();
const lines = vtkCellArray.newInstance();
const polyData = vtkPolyData.newInstance();
polyData.setPoints(points);
polyData.setLines(lines);
state.layers.set(state.currentLayer, {
polyData,
points,
lines,
zHeight: state.lastZ
});
}
}
function addLineToLayer(startPoint, endPoint) {
initializeLayer();
const layer = state.layers.get(state.currentLayer);
// Add points and get their indices
const startIndex = layer.points.insertNextPoint(startPoint[0], startPoint[1], startPoint[2]);
const endIndex = layer.points.insertNextPoint(endPoint[0], endPoint[1], endPoint[2]);
// Add line cell
layer.lines.insertNextCell([startIndex, endIndex]);
}
function processMove(params) {
const newPosition = {
...state.currentPosition
};
let positionChanged = false;
['X', 'Y', 'Z'].forEach(axis => {
if (axis in params) {
const value = state.isMetric ? params[axis] : params[axis] * 25.4;
newPosition[axis.toLowerCase()] = state.isAbsolute ? value + state.offset[axis.toLowerCase()] : state.currentPosition[axis.toLowerCase()] + value;
positionChanged = true;
}
});
if (positionChanged) {
if ('Z' in params) {
detectLayerChange(newPosition.z);
}
const startPoint = [state.currentPosition.x, state.currentPosition.y, state.currentPosition.z];
const endPoint = [newPosition.x, newPosition.y, newPosition.z];
addLineToLayer(startPoint, endPoint);
state.currentPosition = newPosition;
}
}
function processG92(params) {
['X', 'Y', 'Z'].forEach(axis => {
if (axis in params) {
state.offset[axis.toLowerCase()] = state.currentPosition[axis.toLowerCase()] - (state.isMetric ? params[axis] : params[axis] * 25.4);
}
});
}
function processCommand(command, params) {
switch (command) {
case 'G0': // Rapid move
case 'G1':
// Linear move
processMove(params);
break;
case 'G20':
// Imperial
state.isMetric = false;
break;
case 'G21':
// Metric
state.isMetric = true;
break;
case 'G90':
// Absolute positioning
state.isAbsolute = true;
break;
case 'G91':
// Relative positioning
state.isAbsolute = false;
break;
case 'G92':
// Set position
processG92(params);
break;
}
}
function parseGCode(gcodeText) {
const lines = gcodeText.split('\n');
lines.forEach(line => {
const sline = line.split(';')[0].trim();
if (!sline) return;
const tokens = sline.split(' ');
const command = tokens[0];
const params = {};
tokens.slice(1).forEach(token => {
const param = token[0];
const value = parseFloat(token.slice(1));
if (!Number.isNaN(value)) {
params[param] = value;
}
});
processCommand(command, params);
});
}
// Public methods
publicAPI.setUrl = (url, option = {
binary: true
}) => {
model.url = url;
const path = url.split('/');
path.pop();
model.baseURL = path.join('/');
model.compression = option.compression;
return publicAPI.loadData({
progressCallback: option.progressCallback,
binary: !!option.binary
});
};
publicAPI.loadData = (option = {}) => {
const promise = fetchData(model.url, option);
promise.then(publicAPI.parse);
return promise;
};
publicAPI.parseAsText = content => {
parseGCode(content);
};
publicAPI.parseAsArrayBuffer = content => {
const data = BinaryHelper.arrayBufferToString(content);
parseGCode(data);
};
publicAPI.parse = content => {
if (typeof content === 'string') {
publicAPI.parseAsText(content);
} else {
publicAPI.parseAsArrayBuffer(content);
}
state.layers.forEach((layer, i) => {
model.output[i] = layer.polyData;
});
};
publicAPI.requestData = (inData, outData) => {
publicAPI.parse(model.parseData);
};
publicAPI.getNumberOfOutputPorts = () => state.layers.size;
}
const DEFAULT_VALUES = {
// baseURL: null,
// dataAccessHelper: null,
// url: null,
};
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Build VTK API
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['url', 'baseURL']);
macro.setGet(publicAPI, model, ['dataAccessHelper']);
macro.algo(publicAPI, model, 0, 1);
macro.event(publicAPI, model, 'ready');
vtkGCodeReader(publicAPI, model);
}
const newInstance = macro.newInstance(extend, 'vtkGCodeReader');
var vtkGCodeReader$1 = {
extend,
newInstance
};
export { vtkGCodeReader$1 as default, extend, newInstance };