inital commit

master
Sebastian 10 years ago
commit ec18e3998d

3
.gitignore vendored

@ -0,0 +1,3 @@
*.pyc
*.sqlite
media/*

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "past3d.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

@ -0,0 +1,165 @@
# Django settings for past3d project.
import os
BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../')
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': os.path.join(BASE_DIR, "data.sqlite"), # Or path to database file if using sqlite3.
# The following settings are not used with sqlite3:
'USER': '',
'PASSWORD': '',
'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
'PORT': '', # Set to empty string for default.
}
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = True
# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = True
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/var/www/example.com/media/"
MEDIA_ROOT = os.path.join(BASE_DIR,"media")
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = '/media/'
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
STATIC_ROOT = os.path.join(BASE_DIR,"static")
# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = '/static/'
# Additional locations of static files
STATICFILES_DIRS = (
os.path.join(BASE_DIR,"static_common"),
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'InsertSomethingSecretHereBeforeDoingProduktion'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'past3d.urls'
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'past3d.wsgi.application'
TEMPLATE_DIRS = (
os.path.join(BASE_DIR,'templates'),
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
INSTALLED_APPS = (
'django_extensions',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'pastebin',
# Uncomment the next line to enable the admin:
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
)
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}

@ -0,0 +1,26 @@
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'past3d.views.home', name='home'),
# url(r'^past3d/', include('past3d.foo.urls')),
url(r'^3d/', include('pastebin.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

@ -0,0 +1,32 @@
"""
WSGI config for past3d project.
This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.
Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.
"""
import os
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "past3d.settings"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "past3d.settings")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)

@ -0,0 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.contrib import admin
from models import Geometry
class GeometryAdmin(admin.ModelAdmin):
list_display = ['name', 'description', 'date', 'file', 'sourcefile']
admin.site.register(Geometry, GeometryAdmin)

@ -0,0 +1,59 @@
import os.path
import struct
from hashlib import md5
from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
def safe_upload_path(base_dir):
def generate_path(instance, filename):
ext = os.path.splitext(filename)[1]
md5sum = md5()
md5sum.update(instance.name
+ str(datetime.now())
+ filename)
randomname = md5sum.hexdigest()
return os.path.join(base_dir,'%s%s' % (randomname, ext))
return generate_path
class Geometry(models.Model):
name = models.CharField(max_length = 128)
description = models.TextField(blank=True)
user = models.ForeignKey(User, blank=True)
date = models.DateTimeField(auto_now_add=True)
polycount = models.IntegerField(blank=True, default=0)
file = models.FileField(upload_to=safe_upload_path('models'))
sourcefile = models.FileField(upload_to=safe_upload_path('sources'), blank=True)
def get_polycount(self):
if self.polycount == 0:
self.file.open()
self.polycount = 0
if self.file.read(5) != "solid":
self.file.seek(80)
self.polycount = struct.unpack("i",self.file.read(4))[0]
else:
line = self.file.readline()
while line != "":
line = line.strip()
if line.startswith("facet"):
self.polycount += 1
line = self.file.readline()
self.save()
return self.polycount

@ -0,0 +1,126 @@
// 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 = length + (-precisionBits >> 3) - 1;
var curByte = 0;
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");
}
}
};

@ -0,0 +1,2 @@
// stats.js r5 - http://github.com/mrdoob/stats.js
var Stats=function(){var j=0,u=2,r,C=0,E=new Date().getTime(),w=E,f=E,m=0,e=1000,i=0,F,q,c,d,B,k=0,G=1000,a=0,A,t,p,D,l,v=0,o=1000,s=0,h,n,z,g,b,y={fps:{bg:{r:16,g:16,b:48},fg:{r:0,g:255,b:255}},ms:{bg:{r:16,g:48,b:16},fg:{r:0,g:255,b:0}},mem:{bg:{r:48,g:16,b:26},fg:{r:255,g:0,b:128}}};r=document.createElement("div");r.style.fontFamily="Helvetica, Arial, sans-serif";r.style.textAlign="left";r.style.fontSize="9px";r.style.opacity="0.9";r.style.width="80px";r.style.cursor="pointer";r.addEventListener("click",H,false);F=document.createElement("div");F.style.backgroundColor="rgb("+Math.floor(y.fps.bg.r/2)+","+Math.floor(y.fps.bg.g/2)+","+Math.floor(y.fps.bg.b/2)+")";F.style.padding="2px 0px 3px 0px";r.appendChild(F);q=document.createElement("div");q.innerHTML="<strong>FPS</strong>";q.style.color="rgb("+y.fps.fg.r+","+y.fps.fg.g+","+y.fps.fg.b+")";q.style.margin="0px 0px 1px 3px";F.appendChild(q);c=document.createElement("canvas");c.width=74;c.height=30;c.style.display="block";c.style.marginLeft="3px";F.appendChild(c);d=c.getContext("2d");d.fillStyle="rgb("+y.fps.bg.r+","+y.fps.bg.g+","+y.fps.bg.b+")";d.fillRect(0,0,c.width,c.height);B=d.getImageData(0,0,c.width,c.height);A=document.createElement("div");A.style.backgroundColor="rgb("+Math.floor(y.ms.bg.r/2)+","+Math.floor(y.ms.bg.g/2)+","+Math.floor(y.ms.bg.b/2)+")";A.style.padding="2px 0px 3px 0px";A.style.display="none";r.appendChild(A);t=document.createElement("div");t.innerHTML="<strong>MS</strong>";t.style.color="rgb("+y.ms.fg.r+","+y.ms.fg.g+","+y.ms.fg.b+")";t.style.margin="0px 0px 1px 3px";A.appendChild(t);p=document.createElement("canvas");p.width=74;p.height=30;p.style.display="block";p.style.marginLeft="3px";A.appendChild(p);D=p.getContext("2d");D.fillStyle="rgb("+y.ms.bg.r+","+y.ms.bg.g+","+y.ms.bg.b+")";D.fillRect(0,0,p.width,p.height);l=D.getImageData(0,0,p.width,p.height);try{if(webkitPerformance&&webkitPerformance.memory.totalJSHeapSize){u=3}}catch(x){}h=document.createElement("div");h.style.backgroundColor="rgb("+Math.floor(y.mem.bg.r/2)+","+Math.floor(y.mem.bg.g/2)+","+Math.floor(y.mem.bg.b/2)+")";h.style.padding="2px 0px 3px 0px";h.style.display="none";r.appendChild(h);n=document.createElement("div");n.innerHTML="<strong>MEM</strong>";n.style.color="rgb("+y.mem.fg.r+","+y.mem.fg.g+","+y.mem.fg.b+")";n.style.margin="0px 0px 1px 3px";h.appendChild(n);z=document.createElement("canvas");z.width=74;z.height=30;z.style.display="block";z.style.marginLeft="3px";h.appendChild(z);g=z.getContext("2d");g.fillStyle="#301010";g.fillRect(0,0,z.width,z.height);b=g.getImageData(0,0,z.width,z.height);function I(N,M,K){var J,O,L;for(O=0;O<30;O++){for(J=0;J<73;J++){L=(J+O*74)*4;N[L]=N[L+4];N[L+1]=N[L+5];N[L+2]=N[L+6]}}for(O=0;O<30;O++){L=(73+O*74)*4;if(O<M){N[L]=y[K].bg.r;N[L+1]=y[K].bg.g;N[L+2]=y[K].bg.b}else{N[L]=y[K].fg.r;N[L+1]=y[K].fg.g;N[L+2]=y[K].fg.b}}}function H(){j++;j==u?j=0:j;F.style.display="none";A.style.display="none";h.style.display="none";switch(j){case 0:F.style.display="block";break;case 1:A.style.display="block";break;case 2:h.style.display="block";break}}return{domElement:r,update:function(){C++;E=new Date().getTime();k=E-w;G=Math.min(G,k);a=Math.max(a,k);I(l.data,Math.min(30,30-(k/200)*30),"ms");t.innerHTML="<strong>"+k+" MS</strong> ("+G+"-"+a+")";D.putImageData(l,0,0);w=E;if(E>f+1000){m=Math.round((C*1000)/(E-f));e=Math.min(e,m);i=Math.max(i,m);I(B.data,Math.min(30,30-(m/100)*30),"fps");q.innerHTML="<strong>"+m+" FPS</strong> ("+e+"-"+i+")";d.putImageData(B,0,0);if(u==3){v=webkitPerformance.memory.usedJSHeapSize*9.54e-7;o=Math.min(o,v);s=Math.max(s,v);I(b.data,Math.min(30,30-(v/2)),"mem");n.innerHTML="<strong>"+Math.round(v)+" MEM</strong> ("+Math.round(o)+"-"+Math.round(s)+")";g.putImageData(b,0,0)}f=E;C=0}}}};

@ -0,0 +1,318 @@
Thingiloader = function(event) {
// Code from https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data
this.load_binary_resource = function(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
// The following line says we want to receive data as Binary and not as Unicode
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
return req.responseText;
};
this.loadSTL = function(url) {
var looksLikeBinary = function(reader) {
// STL files don't specify a way to distinguish ASCII from binary.
// The usual way is checking for "solid" at the start of the file --
// but Thingiverse has seen at least one binary STL file in the wild
// that breaks this.
// The approach here is different: binary STL files contain a triangle
// count early in the file. If this correctly predicts the file's length,
// it is most probably a binary STL file.
reader.seek(80); // skip the header
var count = reader.readUInt32();
var predictedSize = 80 /* header */ + 4 /* count */ + 50 * count;
return reader.getSize() == predictedSize;
};
workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
var file = this.load_binary_resource(url);
var reader = new BinaryReader(file);
if (looksLikeBinary(reader)) {
this.loadSTLBinary(reader);
} else {
this.loadSTLString(file);
}
};
this.loadOBJ = function(url) {
workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
var file = this.load_binary_resource(url);
this.loadOBJString(file);
};
this.loadJSON = function(url) {
workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
var file = this.load_binary_resource(url);
this.loadJSONString(file);
};
this.loadPLY = function(url) {
workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
var file = this.load_binary_resource(url);
if (file.match(/format ascii/i)) {
this.loadPLYString(file);
} else {
this.loadPLYBinary(file);
}
};
this.loadSTLString = function(STLString) {
workerFacadeMessage({'status':'message', 'content':'Parsing STL String...'});
workerFacadeMessage({'status':'complete', 'content':this.ParseSTLString(STLString)});
};
this.loadSTLBinary = function(STLBinary) {
workerFacadeMessage({'status':'message', 'content':'Parsing STL Binary...'});
workerFacadeMessage({'status':'complete', 'content':this.ParseSTLBinary(STLBinary)});
};
this.loadOBJString = function(OBJString) {
workerFacadeMessage({'status':'message', 'content':'Parsing OBJ String...'});
workerFacadeMessage({'status':'complete', 'content':this.ParseOBJString(OBJString)});
};
this.loadJSONString = function(JSONString) {
workerFacadeMessage({'status':'message', 'content':'Parsing JSON String...'});
workerFacadeMessage({'status':'complete', 'content':eval(JSONString)});
};
this.loadPLYString = function(PLYString) {
workerFacadeMessage({'status':'message', 'content':'Parsing PLY String...'});
workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYString(PLYString)});
};
this.loadPLYBinary = function(PLYBinary) {
workerFacadeMessage({'status':'message', 'content':'Parsing PLY Binary...'});
workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYBinary(PLYBinary)});
};
this.ParsePLYString = function(input) {
var properties = [];
var vertices = [];
var colors = [];
var vertex_count = 0;
var header = /ply\n([\s\S]+)\nend_header/ig.exec(input)[1];
var data = /end_header\n([\s\S]+)$/ig.exec(input)[1];
// workerFacadeMessage({'status':'message', 'content':'header:\n' + header});
// workerFacadeMessage({'status':'message', 'content':'data:\n' + data});
header_parts = header.split("\n");
for (i in header_parts) {
if (/element vertex/i.test(header_parts[i])) {
vertex_count = /element vertex (\d+)/i.exec(header_parts[i])[1];
} else if (/property/i.test(header_parts[i])) {
properties.push(/property (.*) (.*)/i.exec(header_parts[i])[2]);
}
}
// workerFacadeMessage({'status':'message', 'content':'properties: ' + properties});
data_parts = data.split("\n");
for (i in data_parts) {
data_line = data_parts[i];
data_line_parts = data_line.split(" ");
vertices.push([
parseFloat(data_line_parts[properties.indexOf("x")]),
parseFloat(data_line_parts[properties.indexOf("y")]),
parseFloat(data_line_parts[properties.indexOf("z")])
]);
colors.push([
parseInt(data_line_parts[properties.indexOf("red")]),
parseInt(data_line_parts[properties.indexOf("green")]),
parseInt(data_line_parts[properties.indexOf("blue")])
]);
}
// workerFacadeMessage({'status':'message', 'content':'vertices: ' + vertices});
return [vertices, colors];
};
this.ParsePLYBinary = function(input) {
return false;
};
this.ParseSTLBinary = function(input) {
// Skip the header.
input.seek(80);
// Load the number of vertices.
var count = input.readUInt32();
// During the parse loop we maintain the following data structures:
var vertices = []; // Append-only list of all unique vertices.
var vert_hash = {}; // Mapping from vertex to index in 'vertices', above.
var faces = []; // List of triangle descriptions, each a three-element
// list of indices in 'vertices', above.
for (var i = 0; i < count; i++) {
if (i % 100 == 0) {
workerFacadeMessage({
'status':'message',
'content':'Parsing ' + (i+1) + ' of ' + count + ' polygons...'
});
workerFacadeMessage({
'status':'progress',
'content':parseInt(i / count * 100) + '%'
});
}
// Skip the normal (3 single-precision floats)
input.seek(input.getPosition() + 12);
var face_indices = [];
for (var x = 0; x < 3; x++) {
var vertex = [input.readFloat(), input.readFloat(), input.readFloat()];
var vertexIndex = vert_hash[vertex];
if (vertexIndex == null) {
vertexIndex = vertices.length;
vertices.push(vertex);
vert_hash[vertex] = vertexIndex;
}
face_indices.push(vertexIndex);
}
faces.push(face_indices);
// Skip the "attribute" field (unused in common models)
input.readUInt16();
}
return [vertices, faces];
};
// build stl's vertex and face arrays
this.ParseSTLString = function(STLString) {
var vertexes = [];
var faces = [];
var face_vertexes = [];
var vert_hash = {}
// console.log(STLString);
// strip out extraneous stuff
STLString = STLString.replace(/\r/, "\n");
STLString = STLString.replace(/^solid[^\n]*/, "");
STLString = STLString.replace(/\n/g, " ");
STLString = STLString.replace(/facet normal /g,"");
STLString = STLString.replace(/outer loop/g,"");
STLString = STLString.replace(/vertex /g,"");
STLString = STLString.replace(/endloop/g,"");
STLString = STLString.replace(/endfacet/g,"");
STLString = STLString.replace(/endsolid[^\n]*/, "");
STLString = STLString.replace(/\s+/g, " ");
STLString = STLString.replace(/^\s+/, "");
// console.log(STLString);
var facet_count = 0;
var block_start = 0;
var points = STLString.split(" ");
workerFacadeMessage({'status':'message', 'content':'Parsing vertices...'});
for (var i=0; i<points.length/12-1; i++) {
if ((i % 100) == 0) {
workerFacadeMessage({'status':'progress', 'content':parseInt(i / (points.length/12-1) * 100) + '%'});
}
var face_indices = [];
for (var x=0; x<3; x++) {
var vertex = [parseFloat(points[block_start+x*3+3]), parseFloat(points[block_start+x*3+4]), parseFloat(points[block_start+x*3+5])];
var vertexIndex = vert_hash[vertex];
if (vertexIndex == null) {
vertexIndex = vertexes.length;
vertexes.push(vertex);
vert_hash[vertex] = vertexIndex;
}
face_indices.push(vertexIndex);
}
faces.push(face_indices);
block_start = block_start + 12;
}
return [vertexes, faces];
};
this.ParseOBJString = function(OBJString) {
var vertexes = [];
var faces = [];
var lines = OBJString.split("\n");
// var normal_position = 0;
for (var i=0; i<lines.length; i++) {
workerFacadeMessage({'status':'progress', 'content':parseInt(i / lines.length * 100) + '%'});
line_parts = lines[i].replace(/\s+/g, " ").split(" ");
if (line_parts[0] == "v") {
vertexes.push([parseFloat(line_parts[1]), parseFloat(line_parts[2]), parseFloat(line_parts[3])]);
} else if (line_parts[0] == "f") {
faces.push([parseFloat(line_parts[1].split("/")[0])-1, parseFloat(line_parts[2].split("/")[0])-1, parseFloat(line_parts[3].split("/")[0]-1), 0])
}
}
return [vertexes, faces];
};
switch(event.data.cmd) {
case "loadSTL":
this.loadSTL(event.data.param);
break;
case "loadSTLString":
this.loadSTLString(event.data.param);
break;
case "loadSTLBinary":
this.loadSTLBinary(event.data.param);
break;
case "loadOBJ":
this.loadOBJ(event.data.param);
break;
case "loadOBJString":
this.loadOBJString(event.data.param);
break;
case "loadJSON":
this.loadJSON(event.data.param);
break;
case "loadPLY":
this.loadPLY(event.data.param);
break;
case "loadPLYString":
this.loadPLYString(event.data.param);
break;
case "loadPLYBinary":
this.loadPLYBinary(event.data.param);
break;
}
};
if (typeof(window) === "undefined") {
onmessage = Thingiloader;
workerFacadeMessage = postMessage;
importScripts('binaryReader.js');
} else {
workerFacadeMessage = WorkerFacade.add(thingiurlbase + "/thingiloader.js", Thingiloader);
}

</
@ -0,0 +1,984 @@
var Thingiview = function(containerId, gridsize, gridunit) {
this.scope = this;
this.containerId = containerId;
this.gridsize = gridsize;
this.gridunit = gridunit;
this.init();
}
Thingiview.prototype.init = function() {
this.container = document.getElementById(this.containerId);
// this.stats = null;
this.camera = null;
this.scene = null;
this.renderer = null;
this.object = null;
this.plane = null;
this.ambientLight = null;
this.directionalLight = null;
this.pointLight = null;
this.targetXRotation = 0;
this.targetXRotationOnMouseDown = 0;
this.mouseX = 0;
this.mouseXOnMouseDown = 0;
this.targetYRotation = 0;
this.targetYRotationOnMouseDown = 0;
this.mouseY = 0;
this.mouseYOnMouseDown = 0;
this.mouseDown = false;
this.mouseOver = false;
this.windowHalfX = window.innerWidth / 2;
this.windowHalfY = window.innerHeight / 2
this.view = null;
this.infoMessage = null;
this.progressBar = null;
this.alertBox = null;
this.timer = null;
this.rotateTimer = null;
this.rotateListener = null;
this.wasRotating = null;
this.cameraView = 'diagonal';
this.cameraZoom = 0;
this.rotate = false;
this.backgroundColor = '#606060';
this.objectMaterial = 'solid';
this.objectColor = 0xffffff;
this.showPlane = true;
this.isWebGl = false;
if (document.defaultView && document.defaultView.getComputedStyle) {
this.width = parseFloat(document.defaultView.getComputedStyle(this.container,null).getPropertyValue('width'));
this.height = parseFloat(document.defaultView.getComputedStyle(this.container,null).getPropertyValue('height'));
} else {
this.width = parseFloat(this.container.currentStyle.width);
this.height = parseFloat(this.container.currentStyle.height);
}
this.geometry;
}
Thingiview.prototype.initScene = function() {
this.container.style.position = 'relative';
this.container.innerHTML = '';
this.camera = new THREE.PerspectiveCamera(45, this.width/ this.height, 1, 100000);
this.camera.up = new THREE.Vector3(0, 0, 1);
this.camera.target = new THREE.Vector3(0, 0, 0);
this.camera.updateMatrix();
this.scene = new THREE.Scene();
this.ambientLight = new THREE.AmbientLight(0x202020);
this.scene.add(this.ambientLight);
this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.75);
this.directionalLight.position.x = 1;
this.directionalLight.position.y = 1;
this.directionalLight.position.z = 2;
this.directionalLight.position.normalize();
this.scene.add(this.directionalLight);
this.pointLight = new THREE.PointLight(0xffffff, 0.3);
this.pointLight.position.x = 0;
this.pointLight.position.y = -25;
this.pointLight.position.z = 10;
this.scene.add(this.pointLight);
this.progressBar = document.createElement('div');
this.progressBar.style.position = 'absolute';
this.progressBar.style.top = '0px';
this.progressBar.style.left = '0px';
this.progressBar.style.backgroundColor = 'red';
this.progressBar.style.padding = '5px';
this.progressBar.style.display = 'none';
this.progressBar.style.overflow = 'visible';
this.progressBar.style.whiteSpace = 'nowrap';
this.progressBar.style.zIndex = 100;
this.container.appendChild(this.progressBar);
alertBox = document.createElement('div');
alertBox.id = 'alertBox';
alertBox.style.position = 'absolute';
alertBox.style.top = '25%';
alertBox.style.left = '25%';
alertBox.style.width = '50%';
alertBox.style.height = '50%';
alertBox.style.backgroundColor = '#dddddd';
alertBox.style.padding = '10px';
// alertBox.style.overflowY = 'scroll';
alertBox.style.display = 'none';
alertBox.style.zIndex = 100;
this.container.appendChild(alertBox);
// load a blank object
// this.loadSTLString('');
if (this.showPlane) {
this.loadPlaneGeometry();
}
this.setCameraView(this.cameraView);
this.setObjectMaterial(this.objectMaterial);
testCanvas = document.createElement('canvas');
try {
if (testCanvas.getContext('experimental-webgl')) {
// this.showPlane = false;
isWebGl = true;
this.renderer = new THREE.WebGLRenderer();
// this.renderer = new THREE.CanvasRenderer();
} else {
this.renderer = new THREE.CanvasRenderer();
}
} catch(e) {
this.renderer = new THREE.CanvasRenderer();
// log("failed webgl detection");
}
// this.renderer.setSize(this.container.innerWidth, this.container.innerHeight);
this.renderer.setSize(this.width, this.height);
this.renderer.domElement.style.backgroundColor = this.backgroundColor;
this.container.appendChild(this.renderer.domElement);
// stats = new Stats();
// stats.domElement.style.position = 'absolute';
// stats.domElement.style.top = '0px';
// this.container.appendChild(stats.domElement);
// TODO: figure out how to get the render window to resize when window resizes
// window.addEventListener('resize', onContainerResize(), false);
// this.container.addEventListener('resize', onContainerResize(), false);
// this.renderer.domElement.addEventListener('mousemove', onRendererMouseMove, false);
window.addEventListener('mousemove', (function(self) {
return function(event) {
self.onRendererMouseMove(event);
}
})(this), false);
this.renderer.domElement.addEventListener('mouseover', (function(self) {
return function(event) {
self.onRendererMouseOver(event);
}
})(this), false);
this.renderer.domElement.addEventListener('mouseout', (function(self) {
return function(event) {
self.onRendererMouseOut(event);
}
})(this), false);
this.renderer.domElement.addEventListener('mousedown', (function(self) {
return function(event) {
self.onRendererMouseDown(event);
}
})(this), false);
// this.renderer.domElement.addEventListener('mouseup', this.onRendererMouseUp, false);
window.addEventListener('mouseup', (function(self) {
return function(event) {
self.onRendererMouseUp(event);
}
})(this), false);
this.renderer.domElement.addEventListener('touchstart', (function(self) {
return function(event) {
self.onRendererTouchStart(event);
}
})(this), false);
this.renderer.domElement.addEventListener('touchend', (function(self) {
return function(event) {
self.onRendererTouchEnd(event);
}
})(this), false);
this.renderer.domElement.addEventListener('touchmove', (function(self) {
return function(event) {
self.onRendererTouchMove(event);
}
})(this), false);
this.renderer.domElement.addEventListener('DOMMouseScroll', (function(self) {
return function(event) {
self.onRendererScroll(event);
}
})(this), false);
this.renderer.domElement.addEventListener('mousewheel', (function(self) {
return function(event) {
self.onRendererScroll(event);
}
})(this), false);
this.renderer.domElement.addEventListener('gesturechange', (function(self) {
return function(event) {
self.onRendererGestureChange(event);
}
})(this), false);
}
// FIXME
// onContainerResize = function(event) {
// width = parseFloat(document.defaultView.getComputedStyle(this.container,null).getPropertyValue('width'));
// height = parseFloat(document.defaultView.getComputedStyle(this.container,null).getPropertyValue('height'));
//
// // log("resized width: " + width + ", height: " + height);
//
// if (this.renderer) {
// this.renderer.setSize(width, height);
// camera.projectionMatrix = THREE.Matrix4.makePerspective(70, width / height, 1, 10000);
// this.sceneLoop();
// }
// };
Thingiview.prototype.onRendererScroll = function(event) {
event.preventDefault();
var rolled = 0;
if (event.wheelDelta === undefined) {
// Firefox
// The measurement units of the detail and wheelDelta properties are different.
rolled = -40 * event.detail;
} else {
rolled = event.wheelDelta;
}
if (rolled > 0) {
// up
this.scope.setCameraZoom(+10);
} else {
// down
this.scope.setCameraZoom(-10);
}
}
Thingiview.prototype.onRendererGestureChange = function(event) {
event.preventDefault();
if (event.scale > 1) {
this.scope.setCameraZoom(+5);
} else {
this.scope.setCameraZoom(-5);
}
}
Thingiview.prototype.onRendererMouseOver = function(event) {
this.mouseOver = true;
// targetRotation = object.rotation.z;
if (this.timer == null) {
// log('starting loop');
this.timer = setInterval((function(self) {
return function() {
self.sceneLoop();
}
})(this), 1000/60);
}
}
Thingiview.prototype.onRendererMouseDown = function(event) {
// log("down");
event.preventDefault();
this.mouseDown = true;
if(this.scope.getRotation()){
this.wasRotating = true;
this.setRotation(false);
} else {
this.wasRotating = false;
}
mouseXOnMouseDown = event.clientX - this.windowHalfX;
mouseYOnMouseDown = event.clientY - this.windowHalfY;
this.targetXRotationOnMouseDown = this.targetXRotation;
this.targetYRotationOnMouseDown = this.targetYRotation;
}
Thingiview.prototype.onRendererMouseMove = function(event) {
// log("move");
if (this.mouseDown) {
mouseX = event.clientX - this.windowHalfX;
// this.targetXRotation = this.targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
xrot = this.targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
mouseY = event.clientY - this.windowHalfY;
// this.targetYRotation = this.targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
yrot = this.targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
this.targetXRotation = xrot;
this.targetYRotation = yrot;
}
}
Thingiview.prototype.onRendererMouseUp = function(event) {
// log("up");
if (this.mouseDown) {
this.mouseDown = false;
if (!this.mouseOver) {
clearInterval(this.timer);
this.timer = null;
}
if (this.wasRotating) {
this.setRotation(true);
}
}
}
Thingiview.prototype.onRendererMouseOut = function(event) {
if (!this.mouseDown) {
clearInterval(this.timer);
this.timer = null;
}
this.mouseOver = false;
}
Thingiview.prototype.onRendererTouchStart = function(event) {
this.targetXRotation = this.object.rotation.z;
this.targetYRotation = this.object.rotation.x;
this.timer = setInterval((function(self) {
return function() {
self.sceneLoop();
}
})(this), 1000/60);
if (event.touches.length == 1) {
event.preventDefault();
mouseXOnMouseDown = event.touches[0].pageX - this.windowHalfX;
this.targetXRotationOnMouseDown = this.targetXRotation;
mouseYOnMouseDown = event.touches[0].pageY - this.windowHalfY;
this.targetYRotationOnMouseDown = this.targetYRotation;
}
}
Thingiview.prototype.onRendererTouchEnd = function(event) {
clearInterval(this.timer);
this.timer = null;
// this.targetXRotation = this.object.rotation.z;
// this.targetYRotation = this.object.rotation.x;
}
Thingiview.prototype.onRendererTouchMove = function(event) {
if (event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - this.windowHalfX;
this.targetXRotation = this.targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;
mouseY = event.touches[0].pageY - this.windowHalfY;
this.targetYRotation = this.targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.05;
}
}
Thingiview.prototype.sceneLoop = function() {
if (this.object) {
// if (view == 'bottom') {
// if (this.showPlane) {
// this.plane.rotation.z = this.object.rotation.z -= (targetRotation + this.object.rotation.z) * 0.05;
// } else {
// this.object.rotation.z -= (targetRotation + this.object.rotation.z) * 0.05;
// }
// } else {
// if (this.showPlane) {
// this.plane.rotation.z = this.object.rotation.z += (targetRotation - this.object.rotation.z) * 0.05;
// } else {
// this.object.rotation.z += (targetRotation - this.object.rotation.z) * 0.05;
// }
// }
if (this.showPlane) {
this.plane.rotation.z = this.object.rotation.z = (this.targetXRotation - this.object.rotation.z) * 0.2;
this.plane.rotation.x = this.object.rotation.x = (this.targetYRotation - this.object.rotation.x) * 0.2;
} else {
this.object.rotation.z = (this.targetXRotation - this.object.rotation.z) * 0.2;
this.object.rotation.x = (this.targetYRotation - this.object.rotation.x) * 0.2;
}
// log(this.object.rotation.x);
this.camera.lookAt(this.camera.target);
this.camera.updateMatrix();
this.object.updateMatrix();
if (this.showPlane) {
this.plane.updateMatrix();
}
this.renderer.render(this.scene, this.camera);
// stats.update();
}
}
Thingiview.prototype.rotateLoop = function() {
// targetRotation += 0.01;
this.targetXRotation += 0.05;
this.sceneLoop();
}
Thingiview.prototype.getShowPlane = function(){
return this.showPlane;
}
Thingiview.prototype.setShowPlane = function(show) {
this.showPlane = show;
if (show) {
log("Showing plane");
if (this.scene && !this.plane) {
this.loadPlaneGeometry();
}
this.scene.add(this.plane);
this.plane.updateMatrix();
} else {
log("Hiding plane");
if (this.scene && this.plane) {
this.scene.remove(this.plane);
}
}
this.sceneLoop();
}
Thingiview.prototype.getRotation = function() {
return this.rotateTimer !== null;
}
Thingiview.prototype.setRotation = function(rotate) {
log("Rotation set to " + rotate)
clearInterval(this.rotateTimer);
if (rotate) {
this.rotateTimer = setInterval((function(self) {
return function() {
self.rotateLoop();
}
})(this), 1000/60);
} else {
this.rotateTimer = null;
}
this.scope.onSetRotation();
}
Thingiview.prototype.onSetRotation = function(callback) {
if(callback === undefined){
if(this.rotateListener !== null){
try{
this.rotateListener(this.scope.getRotation());
} catch(ignored) {}
}
} else {
this.rotateListener = callback;
}
}
Thingiview.prototype.setCameraView = function(dir) {
this.cameraView = dir;
this.targetXRotation = 0;
this.targetYRotation = 0;
if (this.object) {
this.object.rotation.x = 0;
this.object.rotation.y = 0;
this.object.rotation.z = 0;
}
if (this.showPlane && this.object) {
this.plane.rotation.x = this.object.rotation.x;
this.plane.rotation.y = this.object.rotation.y;
this.plane.rotation.z = this.object.rotation.z;
}
if (dir == 'top') {
// camera.position.y = 0;
// this.camera.position.z = 100;
// this.camera.target.z = 0;
if (this.showPlane) {
this.plane.flipSided = false;
}
} else if (dir == 'side') {
// this.camera.position.y = -70;
// this.camera.position.z = 70;
// this.camera.target.z = 0;
this.targetYRotation = -4.5;
if (this.showPlane) {
this.plane.flipSided = false;
}
} else if (dir == 'bottom') {
// this.camera.position.y = 0;
// this.camera.position.z = -100;
// this.camera.target.z = 0;
if (this.showPlane) {
this.plane.flipSided = true;
}
} else {
// this.camera.position.y = -70;
// this.camera.position.z = 70;
// this.camera.target.z = 0;
if (this.showPlane) {
this.plane.flipSided = false;
}
}
mouseX = this.targetXRotation;
mouseXOnMouseDown = this.targetXRotation;
mouseY = this.targetYRotation;
mouseYOnMouseDown = this.targetYRotation;
this.scope.centerCamera();
this.sceneLoop();
}
Thingiview.prototype.setCameraZoom = function(factor) {
this.cameraZoom = factor;
if (this.cameraView == 'bottom') {
if (this.camera.position.z + factor > 0) {
factor = 0;
}
} else {
if (this.camera.position.z - factor < 0) {
factor = 0;
}
}
if (this.cameraView == 'top') {
this.camera.position.z -= factor;
} else if (this.cameraView == 'bottom') {
this.camera.position.z += factor;
} else if (this.cameraView == 'side') {
this.camera.position.y += factor;
this.camera.position.z -= factor;
} else {
this.camera.position.y += factor;
this.camera.position.z -= factor;
}
this.sceneLoop();
}
Thingiview.prototype.getObjectMaterial = function() {
return this.objectMaterial;
}
Thingiview.prototype.setObjectMaterial = function(type) {
this.objectMaterial = type;
this.loadObjectGeometry();
}
Thingiview.prototype.setBackgroundColor = function(color) {
backgroundColor = color
if (this.renderer) {
this.renderer.domElement.style.backgroundColor = color;
}
}
Thingiview.prototype.setObjectColor = function(color) {
this.objectColor = parseInt(color.replace(/\#/g, ''), 16);
this.loadObjectGeometry();
}
Thingiview.prototype.loadSTL = function(url) {
this.scope.newWorker('loadSTL', url);
}
Thingiview.prototype.loadOBJ = function(url) {
this.scope.newWorker('loadOBJ', url);
}
Thingiview.prototype.loadSTLString = function(STLString) {
this.scope.newWorker('loadSTLString', STLString);
}
Thingiview.prototype.loadSTLBinary = function(STLBinary) {
this.scope.newWorker('loadSTLBinary', STLBinary);
}
Thingiview.prototype.loadOBJString = function(OBJString) {
this.scope.newWorker('loadOBJString', OBJString);
}
Thingiview.prototype.loadJSON = function(url) {
this.scope.newWorker('loadJSON', url);
}
Thingiview.prototype.loadPLY = function(url) {
this.scope.newWorker('loadPLY', url);
}
Thingiview.prototype.loadPLYString = function(PLYString) {