Initial commit

This commit is contained in:
Ravi Riley 2020-04-28 04:00:09 -07:00
commit a5cb6f33e0
8 changed files with 11456 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Ravi Riley
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# STL-to-OpenSCAD-Converter

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<!-- based on http://jsfiddle.net/roha/353r2k8w/ -->
<head>
<title>STL to OpenSCAD Converter</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery-1.12.4.js"></script>
<script src="binaryReader.js"></script>
</head>
<body>
<p>
Convert STL files to open SCAD, read more about it here:
<a href='https://www.thingiverse.com/thing:1383325' target=_new>Thingiverse: STL to OpenSCAD converter</a>
</p>
<div><span id="error"></span></div>
<div><input type="file" id="files" name="file"/></div>
<button onclick="abortRead();">Cancel read</button>
<div id="progress_bar">
<div class="percent">0%</div>
</div>
<span id="conversion"></span>
<span id="result"></span>
<div>
<button>
<a href="#">Download</a>
</button>
</div>
<script src="main.js"></script>
</body>
</html>

127
binaryReader.js Normal file
View File

@ -0,0 +1,127 @@
//obtained from:
// http://blog.vjeux.com/wp-content/uploads/2010/01/binaryReader.js
// BinaryReader
// Refactored by Vjeux <vjeuxx@gmail.com>
// http://blog.vjeux.com/2010/javascript/javascript-binary-reader.html
// Original
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/classes/binary-parser [rev. #1]
BinaryReader = function (data) {
this._buffer = data;
this._pos = 0;
};
BinaryReader.prototype = {
/* Public */
readInt8: function (){ return this._decodeInt(8, true); },
readUInt8: function (){ return this._decodeInt(8, false); },
readInt16: function (){ return this._decodeInt(16, true); },
readUInt16: function (){ return this._decodeInt(16, false); },
readInt32: function (){ return this._decodeInt(32, true); },
readUInt32: function (){ return this._decodeInt(32, false); },
readFloat: function (){ return this._decodeFloat(23, 8); },
readDouble: function (){ return this._decodeFloat(52, 11); },
readChar: function () { return this.readString(1); },
readString: function (length) {
this._checkSize(length * 8);
var result = this._buffer.substr(this._pos, length);
this._pos += length;
return result;
},
seek: function (pos) {
this._pos = pos;
this._checkSize(0);
},
getPosition: function () {
return this._pos;
},
getSize: function () {
return this._buffer.length;
},
/* Private */
_decodeFloat: function(precisionBits, exponentBits){
var length = precisionBits + exponentBits + 1;
var size = length >> 3;
this._checkSize(length);
var bias = Math.pow(2, exponentBits - 1) - 1;
var signal = this._readBits(precisionBits + exponentBits, 1, size);
var exponent = this._readBits(precisionBits, exponentBits, size);
var significand = 0;
var divisor = 2;
var curByte = 0; //length + (-precisionBits >> 3) - 1;
do {
var byteValue = this._readByte(++curByte, size);
var startBit = precisionBits % 8 || 8;
var mask = 1 << startBit;
while (mask >>= 1) {
if (byteValue & mask) {
significand += 1 / divisor;
}
divisor *= 2;
}
} while (precisionBits -= startBit);
this._pos += size;
return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
: (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
: Math.pow(2, exponent - bias) * (1 + significand) : 0);
},
_decodeInt: function(bits, signed){
var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits);
var result = signed && x >= max / 2 ? x - max : x;
this._pos += bits / 8;
return result;
},
//shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
_shl: function (a, b){
for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
return a;
},
_readByte: function (i, size) {
return this._buffer.charCodeAt(this._pos + size - i - 1) & 0xff;
},
_readBits: function (start, length, size) {
var offsetLeft = (start + length) % 8;
var offsetRight = start % 8;
var curByte = size - (start >> 3) - 1;
var lastByte = size + (-(start + length) >> 3);
var diff = curByte - lastByte;
var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);
if (diff && offsetLeft) {
sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;
}
while (diff) {
sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);
}
return sum;
},
_checkSize: function (neededBits) {
if (!(this._pos + Math.ceil(neededBits / 8) < this._buffer.length)) {
throw new Error("Index out of bound");
}
}
};

11008
jquery-1.12.4.js vendored Normal file

File diff suppressed because it is too large Load Diff

238
main.js Normal file
View File

@ -0,0 +1,238 @@
//STL to OpenSCAD converter
//This code will read an STL file and Generate an OpenSCAD file based on the content
//it supports both ASCII and Binary STL files.
var reader;
var progress = document.querySelector('.percent');
var vertices = [];
var triangles = [];
var modules = '';
var calls = '';
var vertexIndex = 0;
var converted = 0;
var totalObjects = 0;
var convertedObjects = 0;
function _reset() {
vertices = [];
triangles = [];
modules = '';
calls = '';
vertexIndex = 0;
converted = 0;
totalObjects = 0;
document.getElementById('error').innerText = '';
document.getElementById('conversion').innerText = '';
}
//stl: the stl file context as a string
//parseResult: This function checks if the file is ASCII or Binary, and parses the file accordingly
function parseResult(stl) {
_reset();
var isAscii = true;
for (var i = 0; i < stl.length; i++) {
if (stl[i].charCodeAt(0) == 0) {
isAscii = false;
break;
}
}
if (!isAscii) {
parseBinaryResult(stl);
} else {
parseAsciiResult(stl);
}
}
function parseBinaryResult(stl) {
//This makes more sense if you read http://en.wikipedia.org/wiki/STL_(file_format)#Binary_STL
var br = new BinaryReader(stl);
br.seek(80); //Skip header
var totalTriangles = br.readUInt32(); //Read # triangles
for (var tr = 0; tr < totalTriangles; tr++) {
try {
document.getElementById('conversion').innerText = 'In Progress - Converted ' + (++converted) + ' out of ' + totalTriangles + ' triangles!';
/*
REAL32[3] Normal vector
REAL32[3] Vertex 1
REAL32[3] Vertex 2
REAL32[3] Vertex 3
UINT16 Attribute byte count*/
//Skip Normal Vector;
br.readFloat();
br.readFloat();
br.readFloat(); //SKIP NORMAL
//Parse every 3 subsequent floats as a vertex
var v1 = '[' + br.readFloat() + ',' + br.readFloat() + ',' + br.readFloat() + ']';
var v2 = '[' + br.readFloat() + ',' + br.readFloat() + ',' + br.readFloat() + ']';
var v3 = '[' + br.readFloat() + ',' + br.readFloat() + ',' + br.readFloat() + ']';
//every 3 vertices create a triangle.
var triangle = '[' + (vertexIndex++) + ',' + (vertexIndex++) + ',' + (vertexIndex++) + ']';
br.readUInt16();
//Add 3 vertices for every triangle
//TODO: OPTIMIZE: Check if the vertex is already in the array, if it is just reuse the index
vertices.push(v1);
vertices.push(v2);
vertices.push(v3);
triangles.push(triangle);
} catch (err) {
error(err);
return;
}
}
saveResult(vertices, triangles);
}
function parseAsciiResult(stl) {
//Find all models
var objects = stl.split('endsolid');
for (var o = 0; o < objects.length; o++) {
//Translation: a non-greedy regex for loop {...} endloop pattern
var patt = /\bloop[\s\S]*?\endloop/mgi;
var result = 'matches are: ';
converted = 0;
match = objects[o].match(patt);
if (match == null) continue;
for (var i = 0; i < match.length; i++) {
try {
document.getElementById('conversion').innerText = 'In Progress - Object ' + (o + 1) + ' out of ' + objects.length + ' Converted ' + (++converted) + ' out of ' + match.length + ' facets!';
//3 different vertex objects each with 3 numbers.
var vpatt = /\bvertex\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s*vertex\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s*vertex\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s+(-?\d+\.?\d*\E?\e?\-?\+?\d*\.?\d*)\s*/mgi;
var v = vpatt.exec(match[i]);
if (v == null) continue;
if (v.length != 10) {
document.getElementById('error').innerText = '\r\nFailed to parse ' + match[i];
break;
}
var v1 = '[' + v[1] + ',' + v[2] + ',' + v[3] + ']';
var v2 = '[' + v[4] + ',' + v[5] + ',' + v[6] + ']';
var v3 = '[' + v[7] + ',' + v[8] + ',' + v[9] + ']';
var triangle = '[' + (vertexIndex++) + ',' + (vertexIndex++) + ',' + (vertexIndex++) + ']';
//Add 3 vertices for every triangle
//TODO: OPTIMIZE: Check if the vertex is already in the array, if it is just reuse the index
vertices.push(v1);
vertices.push(v2);
vertices.push(v3);
triangles.push(triangle);
} catch (err) {
error(err);
return;
}
}
saveResult(vertices, triangles);
}
}
function error(err) {
document.getElementById('error').innerText = "An Error has occured while trying to convert your file!\r\nPlease make sure this is a valid STL file.";
document.getElementById('conversion').innerText = '';
}
//Input: Set of vertices and triangles, both are strings
//Makes the Download link create an OpenScad file with a polyhedron object that represents the parsed stl file
function saveResult(vertices, triangles) {
var poly = 'polyhedron(\r\n points=[' + vertices + ' ],\r\nfaces=[' + triangles + ']);';
calls = calls + 'object' + (++totalObjects) + '(1);\r\n\r\n';
modules = modules + 'module object' + totalObjects + '(scale) {';
modules = modules + poly + '}\r\n\r\n';
result = modules + calls;
window.URL = window.URL || window.webkitURL;
prompt("scad", result);
var blob = new Blob([result], {
type: 'text/plain'
});
$('a').attr("href", window.URL.createObjectURL(blob));
$('a').attr("download", "FromSTL.SCAD");
document.getElementById('conversion').innerText = 'Conversion complete - Click the download link to download your OpenSCAD file! Total Triangles: ' + triangles.length;
}
function errorHandler(evt) {
switch (evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
alert('File Not Found!');
break;
case evt.target.error.NOT_READABLE_ERR:
alert('File is not readable');
break;
case evt.target.error.ABORT_ERR:
break; // noop
default:
alert('An error occurred reading this file.');
};
}
function updateProgress(evt) {
// evt is an ProgressEvent.
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
// Increase the progress bar length.
if (percentLoaded < 100) {
progress.style.width = percentLoaded + '%';
progress.textContent = percentLoaded + '%';
}
}
}
function handleFileSelect(evt) {
// Reset progress indicator on new file selection.
progress.style.width = '0%';
progress.textContent = '0%';
var extension = String(evt).match(/\.[0-9a-z]+$/i);
console.log(extension);
console.log(evt);
console.log(evt.target.baseURI);
//if
reader = new FileReader();
reader.onerror = errorHandler;
reader.onprogress = updateProgress;
reader.onabort = function(e) {
alert('File read cancelled');
};
reader.onloadstart = function(e) {
document.getElementById('progress_bar').className = 'loading';
};
reader.onload = function(e) {
// Ensure that the progress bar displays 100% at the end.
progress.style.width = '100%';
progress.textContent = '100%';
setTimeout("document.getElementById('progress_bar').className='';", 2000);
parseResult(reader.result);
}
// Read in the stl file as a binary string.
reader.readAsBinaryString(evt.target.files[0]);
//}endif?
}
function abortRead() {
reader.abort();
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);

28
style.css Normal file
View File

@ -0,0 +1,28 @@
body {
font-family: Helvetica, Verdana
}
p {
padding: 7px 10px;
}
#error {
color: red;
}
#progress_bar {
margin: 10px 0;
padding: 3px;
border: 1px solid #000;
font-size: 14px;
clear: both;
opacity: 0;
-moz-transition: opacity 1s linear;
-o-transition: opacity 1s linear;
-webkit-transition: opacity 1s linear;
}
#progress_bar.loading {
opacity: 1.0;
}
#progress_bar .percent {
background-color: #99ccff;
height: auto;
width: 0;
}