Skip to content
/ vtk-js Public
forked from Kitware/vtk-js

Commit

Permalink
feat(objwriter): add support for vtkOBJWriter
Browse files Browse the repository at this point in the history
  • Loading branch information
daker committed May 8, 2022
1 parent 51fcdf6 commit 68353a6
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 0 deletions.
69 changes: 69 additions & 0 deletions Sources/IO/Misc/OBJWriter/example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'vtk.js/Sources/favicon';

// Load the rendering pieces we want to use (for both WebGL and WebGPU)
import 'vtk.js/Sources/Rendering/Profiles/Geometry';

import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow';
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';

import vtkOBJWriter from 'vtk.js/Sources/IO/Misc/OBJWriter';
import vtkOBJReader from 'vtk.js/Sources/IO/Misc/OBJReader';
import vtkPolyDataReader from 'vtk.js/Sources/IO/Legacy/PolyDataReader';
// ----------------------------------------------------------------------------
// Standard rendering code setup
// ----------------------------------------------------------------------------

const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
background: [0.5, 0.5, 0.5],
});
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();

const resetCamera = renderer.resetCamera;
const render = renderWindow.render;

const reader = vtkPolyDataReader.newInstance();
const writerReader = vtkOBJReader.newInstance();
const writer = vtkOBJWriter.newInstance();

reader
.setUrl(`${__BASE_PATH__}/data/legacy/sphere.vtk`, { loadData: true })
.then(() => {
writer.setInputData(reader.getOutputData());
const fileContents = writer.getOutputData();
// Can also use a static function to write to OBJ:
// const fileContents = vtkOBJWriter.writeOBJ(reader.getOutputData());

// Display the resulting OBJ
writerReader.parseAsText(fileContents);

const polydata = reader.getOutputData(0);
const mapper = vtkMapper.newInstance();
const actor = vtkActor.newInstance();
actor.setMapper(mapper);
mapper.setInputData(polydata);

renderer.addActor(actor);
resetCamera();
render();

// Add a download link for it
const blob = new Blob([fileContents], { type: 'application/octet-steam' });
const a = window.document.createElement('a');
a.href = window.URL.createObjectURL(blob, {
type: 'application/octet-steam',
});
a.download = 'sphere.obj';
a.text = 'Download';
a.style.position = 'absolute';
a.style.left = '50%';
a.style.bottom = '10px';
document.body.appendChild(a);
a.style.background = 'white';
a.style.padding = '5px';
});

global.writer = writer;
global.writerReader = writerReader;
global.fullScreenRenderer = fullScreenRenderer;
52 changes: 52 additions & 0 deletions Sources/IO/Misc/OBJWriter/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import vtkPolyData from "../../../Common/DataModel/PolyData";
import { vtkAlgorithm, vtkObject } from "../../../interfaces";

/**
*
*/
export interface IOBJWriterInitialValues {}

type vtkOBJWriterBase = vtkObject & vtkAlgorithm;

export interface vtkOBJWriter extends vtkOBJWriterBase {

/**
*
* @param inData
* @param outData
*/
requestData(inData: any, outData: any): void;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkOBJWriter characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IOBJWriterInitialValues} [initialValues] (default: {})
*/
export function extend(publicAPI: object, model: object, initialValues?: IOBJWriterInitialValues): void;

/**
* Method used to create a new instance of vtkOBJWriter
* @param {IOBJWriterInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(initialValues?: IOBJWriterInitialValues): vtkOBJWriter;

/**
*
* @param {vktPolyData} polyData
*/
export function writeOBJ(polyData: vtkPolyData): vtkPolyData;

/**
* vtkOBJWriter writes wavefront obj (.obj) files in ASCII form. OBJ files
* contain the geometry including lines, triangles and polygons. Normals and
* texture coordinates on points are also written if they exist.
*/
export declare const vtkOBJWriter: {
newInstance: typeof newInstance;
extend: typeof extend;
writeOBJ: typeof writeOBJ;
}
export default vtkOBJWriter;
179 changes: 179 additions & 0 deletions Sources/IO/Misc/OBJWriter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import macro from 'vtk.js/Sources/macros';

const { vtkErrorMacro } = macro;

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// vtkOBJWriter methods
// ----------------------------------------------------------------------------

const writeFaces = (faces, withNormals, withTCoords) => {
let outputData = '';
const fd = faces.getData();

let offset = 0;
while (offset < fd.length) {
const faceSize = fd[offset++];
outputData += 'f';
for (let i = 0; i < faceSize; i++) {
outputData += ` ${fd[offset + i] + 1}`;
if (withTCoords) {
outputData += `/${fd[offset + i] + 1}`;
if (withNormals) {
outputData += `//${fd[offset + i] + 1}`;
}
} else if (withNormals) {
outputData += `//${fd[offset + i] + 1}`;
}
}
offset += faceSize;
outputData += '\n';
}
return outputData;
};

const writeLines = (lines) => {
let outputData = '';
const ld = lines.getData();

let offset = 0;
while (offset < ld.length) {
const lineSize = ld[offset++];
outputData += 'l';
for (let i = 0; i < lineSize; i++) {
outputData += ` ${ld[offset + i] + 1}`;
}
offset += lineSize;
outputData += '\n';
}

return outputData;
};

const writePoints = (pts, normals, tcoords) => {
let outputData = '';
const nbPts = pts.getNumberOfPoints();

let p;

// Positions
for (let i = 0; i < nbPts; i++) {
p = pts.getPoint(i, p);
outputData += `v ${p[0]} ${p[1]} ${p[2]}\n`;
}

// Normals
if (normals) {
for (let i = 0; i < nbPts; i++) {
p = normals.getTuple(i, p);
outputData += `vn ${p[0]} ${p[1]} ${p[2]}\n`;
}
}

// Textures
if (tcoords) {
for (let i = 0; i < nbPts; i++) {
p = tcoords.getTuple(i, p);
outputData += `vt ${p[0]} ${p[1]}\n`;
}
}
return outputData;
};

const writeOBJ = (polyData) => {
let outputData = '# Generated by Visualization Toolkit\n';
const pts = polyData.getPoints();
const polys = polyData.getPolys();
const strips = polyData.getStrips() ? polyData.getStrips().getData() : null;
const lines = polyData.getLines();

const normals = polyData.getPointData().getNormals();
const tcoords = polyData.getPointData().getTCoords();

const hasPtNormals = normals !== null;
const hasPtTCoords = tcoords !== null;

if (!pts) {
vtkErrorMacro('No data to write!');
}

// Write points
outputData += writePoints(pts, normals, tcoords);

// Unsupported triangle strips
if (strips && strips.length > 0) {
vtkErrorMacro('Unsupported strips');
}

// Write polygons.
if (polys) {
outputData += writeFaces(polys, hasPtNormals, hasPtTCoords);
}

// Write lines.
if (lines) {
outputData += writeLines(lines);
}

return outputData;
};

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------

export const STATIC = {
writeOBJ,
};

// ----------------------------------------------------------------------------
// vtkOBJWriter methods
// ----------------------------------------------------------------------------

function vtkOBJWriter(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkOBJWriter');

publicAPI.requestData = (inData, outData) => {
const input = inData[0];

if (!input || !input.isA('vtkPolyData')) {
vtkErrorMacro('Invalid or missing input');
return;
}

outData[0] = writeOBJ(input);
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Make this a VTK object
macro.obj(publicAPI, model);

// Also make it an algorithm with one input and one output
macro.algo(publicAPI, model, 1, 1);

// Object specific methods
vtkOBJWriter(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkOBJWriter');

// ----------------------------------------------------------------------------

export default { newInstance, extend, ...STATIC };

0 comments on commit 68353a6

Please sign in to comment.