commit
ec18e3998d
@ -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) {
|