summaryrefslogtreecommitdiff
path: root/assets/javascripts/mx
diff options
context:
space:
mode:
authorpepperpepperpepper <pepper@scannerjammer.com>2015-10-28 17:00:52 -0700
committerpepperpepperpepper <pepper@scannerjammer.com>2015-10-28 17:00:52 -0700
commit3d17f2b534c04ffa3996cd309056180e72408c01 (patch)
treef420b255bb567fabb91fef31d5b98f06cdd87a7b /assets/javascripts/mx
Diffstat (limited to 'assets/javascripts/mx')
-rw-r--r--assets/javascripts/mx/extensions/mx.scene.js165
-rw-r--r--assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js130
-rw-r--r--assets/javascripts/mx/mx.skew.js610
-rw-r--r--assets/javascripts/mx/primitives/mx.image.js50
4 files changed, 955 insertions, 0 deletions
diff --git a/assets/javascripts/mx/extensions/mx.scene.js b/assets/javascripts/mx/extensions/mx.scene.js
new file mode 100644
index 0000000..8f11fb0
--- /dev/null
+++ b/assets/javascripts/mx/extensions/mx.scene.js
@@ -0,0 +1,165 @@
+// NOTE
+//
+// This is not a fully functional 3d scene as you might expect.
+// The camera can only do pitch (rotationX) and yaw (rotationY), but no roll (rotationZ)
+// because I haven't implemented alternative euler orders or quaternions.
+//
+// For serious 3D scenes with more functionalities you should use
+// THREE.js with CSS3D Renderer.
+
+MX.Camera = MX.Object3D.extend({
+
+ init: function(){
+ this.el = null
+ this.type = "Camera"
+ },
+
+ move: function(s){
+ for (var i in s) {
+ this[i] = s[i]
+ }
+ },
+
+ toString: function(){
+ var params = "x y z rotationX rotationY".split(" ")
+ return this.__toString(params, "scene.camera.move")
+ },
+
+ getCameraEuler: function (target) {
+ var dx = target.x - this.x,
+ dy = target.y - this.y,
+ dz = target.z - this.z
+ r = {}
+ r.y = Math.atan2(-dx, dz)
+ r.x = Math.atan2(-dy, Math.sqrt(dx*dx + dz*dz))
+ r.z = 0
+ if (MX.rotationUnit === 'deg') {
+ r.x = MX.toDeg(r.x)
+ r.y = MX.toDeg(r.y)
+ }
+ return r
+ }
+
+})
+
+MX.Scene = (function () {
+
+ var add = MX.Object3D.prototype.add,
+ remove = MX.Object3D.prototype.remove
+
+ function Scene () {
+
+ this.el = document.createElement('div')
+ this.el.classList.add('mx-scene')
+
+ var s = this.el.style
+
+ s[MX.transformProp] = 'preserve-3d'
+
+ s.webkitPerspectiveOrigin = '50% 50%'
+ s.mozPerspectiveOrigin = '50% 50%'
+ s.perspectiveOrigin = '50% 50%'
+
+ s.webkitUserSelect = 'none'
+ s.mozUserSelect = 'none'
+ s.userSelect = 'none'
+
+ s.overflow = 'hidden'
+
+ this.inner = new MX.Object3D().addTo(this.el)
+ this.inner.el.style.width = '0'
+ this.inner.el.style.height = '0'
+
+ var self = this
+ var width, height, perspective
+
+ Object.defineProperty(this, 'width', {
+ get: function () {
+ return width
+ },
+ set: function (val) {
+ width = val
+ self.el.style.width = val + 'px'
+ }
+ })
+
+ Object.defineProperty(this, 'height', {
+ get: function () {
+ return height
+ },
+ set: function (val) {
+ height = val
+ self.el.style.height = val + 'px'
+ }
+ })
+
+ Object.defineProperty(this, 'perspective', {
+ get: function () {
+ return perspective
+ },
+ set: function (val) {
+ perspective = val
+ self.el.style[MX.perspectiveProp] = val + 'px'
+ self.inner.z = -val - self.camera.z
+ self.inner.rotationOrigin.z = -val
+ }
+ })
+
+ var cam = this.camera = new MX.Camera()
+
+ this.inner.rotationOrigin = { x:0, y:0, z:0 }
+
+ this.perspective = 0
+ }
+
+ Scene.prototype = {
+
+ constructor: Scene,
+
+ add: function () {
+ add.apply(this.inner, arguments)
+ return this
+ },
+
+ remove: function () {
+ remove.apply(this.inner, arguments)
+ return this
+ },
+
+ addTo: function (target) {
+ if (typeof target === 'string') {
+ target = document.querySelector(target)
+ }
+ if (target instanceof HTMLElement && target.appendChild) {
+ target.appendChild(this.el)
+ } else {
+ console.warn('You can only add a Scene to an HTML element.')
+ }
+ return this
+ },
+
+ update: function () {
+ // update inner based on camera
+
+ var i = this.inner,
+ c = this.camera
+
+ c.update()
+
+ i.z = -this.perspective - c.z
+ i.x = -c.x
+ i.y = -c.y
+
+ i.rotationX = -c.rotationX
+ i.rotationY = -c.rotationY
+ //i.rotationZ = -c.rotationZ
+
+ i.update()
+ return this
+ },
+
+ }
+
+ return Scene
+
+})() \ No newline at end of file
diff --git a/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js b/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js
new file mode 100644
index 0000000..28b1aac
--- /dev/null
+++ b/assets/javascripts/mx/extensions/mx.unclampedOrbitCamera.js
@@ -0,0 +1,130 @@
+MX.OrbitCamera = function(opt){
+ var exports = {}, bound = false
+ exports.opt = opt = defaults(opt, {
+ el: window, // object to bind events on
+ camera: scene.camera, // camera object we'll be moving
+ radius: 100,
+ radiusRange: [ 10, 1000 ],
+ rotationX: PI/2,
+ rotationY: 0,
+ center: { x: 0, y: 0, z: 0 },
+ sensitivity: 10, // moving 1 pixel is like moving N radians
+ wheelSensitivity: 10,
+ ease: 10,
+ wheelEase: 10,
+ })
+ var rx, ry, radius, px, py, epsilon = 1e-10
+ exports.dragging = false
+ exports.init = function(){
+ ry = opt.rotationY
+ rx = opt.rotationX
+ radius = opt.radius
+ exports.wheel = new wheel({
+ el: opt.el,
+ update: function(e, delta){
+ opt.radius = clamp( opt.radius + delta * opt.wheelSensitivity, opt.radiusRange[0], opt.radiusRange[1] )
+ },
+ })
+ exports.bind()
+ }
+ exports.toggle = function(state){
+ if (state) exports.bind()
+ else exports.unbind()
+ }
+ exports.bind = function(){
+ if (bound) return;
+ bound = true
+ opt.el.addEventListener("mousedown", down)
+ window.addEventListener("mousemove", move)
+ window.addEventListener("mouseup", up)
+ opt.el.addEventListener("touchstart", touch(down))
+ window.addEventListener("touchmove", touch(move))
+ window.addEventListener("touchend", touch(up))
+ exports.wheel.unlock()
+ }
+ exports.unbind = function(){
+ if (! bound) return;
+ bound = false
+ opt.el.removeEventListener("mousedown", down)
+ window.removeEventListener("mousemove", move)
+ window.removeEventListener("mouseup", up)
+ exports.wheel.lock()
+ }
+ function cancelable (fn) {
+ return function(e){
+ e.preventDefault()
+ fn(e)
+ }
+ }
+ function touch (fn){
+ return function(e){
+ fn(e.touches[0])
+ }
+ }
+ function down (e) {
+ px = e.pageX
+ py = e.pageY
+ exports.dragging = true
+ }
+ function move (e) {
+ if (! exports.dragging) return
+ exports.delta(px- e.pageX, py - e.pageY)
+ px = e.pageX
+ py = e.pageY
+ }
+ function up (e) {
+ exports.dragging = false
+ }
+ exports.delta = function(x,y){
+ opt.rotationY += x/window.innerWidth * opt.sensitivity
+ opt.rotationX = opt.rotationX + y/window.innerHeight * opt.sensitivity
+ }
+ exports.move = function(y, x){
+ opt.rotationY = y
+ if (typeof x == "number") { opt.rotationX = x }
+ }
+ exports.zoom = function(r){
+ opt.radius = r
+ }
+ exports.setZoom = function(r){
+ radius = opt.radius = r
+ }
+ exports.zoomPercent = function(n){
+ opt.radius = lerp(n, opt.radiusRange[0], opt.radiusRange[1])
+ }
+ exports.zoomDelta = function(r){
+ opt.radius += r
+ }
+ exports.pause = function(){
+ var sy = sign(opt.rotationY-ry)
+ var sx = sign(opt.rotationX-rx)
+ opt.rotationY = ry + sy * 0.1
+ opt.rotationX = rx + sx * 0.1
+ }
+ exports.update = function(){
+ if (abs(ry - opt.rotationY) > epsilon) {
+ ry = avg(ry, opt.rotationY, opt.ease)
+ }
+ else {
+ ry = opt.rotationY
+ }
+ if (abs(rx - opt.rotationX) > epsilon) {
+ rx = avg(rx, opt.rotationX, opt.ease)
+ }
+ else {
+ rx = opt.rotationX
+ }
+ if (abs(radius - opt.radius) > epsilon) {
+ radius = avg(radius, opt.radius, opt.wheelEase)
+ }
+ else {
+ radius = opt.radius
+ }
+ opt.camera.x = opt.center.x + radius * sin(rx) * cos(ry)
+ opt.camera.z = opt.center.y + radius * sin(rx) * sin(ry)
+ opt.camera.y = opt.center.z + radius * cos(rx)
+ opt.camera.rotationX = PI/2 - rx
+ opt.camera.rotationY = ry + PI/2
+ }
+ return exports
+}
diff --git a/assets/javascripts/mx/mx.skew.js b/assets/javascripts/mx/mx.skew.js
new file mode 100644
index 0000000..73296bd
--- /dev/null
+++ b/assets/javascripts/mx/mx.skew.js
@@ -0,0 +1,610 @@
+/**
+ * Copyright (C) 2013 by Evan You
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+var MX = MX || (function (undefined) {
+
+ var MX = {
+ version: '0.1.0',
+ prefix: undefined,
+ rotationUnit: 'rad',
+ }
+
+ var floatPrecision = 5
+
+ // ========================================================================
+ // Setup & Compatibility
+ // ========================================================================
+
+ var transformProp,
+ transitionProp,
+ transformOriginProp,
+ transformStyleProp,
+ perspectiveProp,
+ transitionEndEvent
+
+ var positionAtCenter = true, // whether to auto center objects
+ centeringCSS // styles to inject for center positioning
+
+ document.addEventListener('DOMContentLoaded', setup)
+
+ function setup () {
+
+ // sniff prefix
+
+ var s = document.body.style
+
+ MX.prefix =
+ 'webkitTransform' in s ? 'webkit' :
+ 'mozTransform' in s ? 'moz' :
+ 'msTransform' in s ? 'ms' : ''
+
+ transformProp = MX.transformProp = addPrefix('transform')
+ transitionProp = MX.transitionProp = addPrefix('transition')
+ transformOriginProp = MX.transformOriginProp = addPrefix('transformOrigin')
+ transformStyleProp = MX.transformStyleProp = addPrefix('transformStyle')
+ perspectiveProp = MX.perspectiveProp = addPrefix('perspective')
+ transitionEndEvent = MX.transitionEndEvent = MX.prefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend'
+
+ // shiv rAF
+
+ var vendors = ['webkit', 'moz', 'ms']
+ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']
+ window.cancelAnimationFrame =
+ window[vendors[x]+'CancelAnimationFrame'] ||
+ window[vendors[x]+'CancelRequestAnimationFrame']
+ }
+
+ // inject centering css
+
+ centeringCSS = document.createElement('style')
+ centeringCSS.type = 'text/css'
+ centeringCSS.innerHTML =
+ '.mx-object3d {'
+ + 'position: absolute;'
+ + 'top: 50%;'
+ + 'left: 50%;}'
+ injectCenteringCSS()
+
+ window.scrollTo(0,0)
+ }
+
+ function injectCenteringCSS () {
+ document.head.appendChild(centeringCSS)
+ }
+
+ function removeCenteringCSS () {
+ document.head.removeChild(centeringCSS)
+ }
+
+ // ========================================================================
+ // Utils
+ // ========================================================================
+
+ function toDeg (rad) {
+ return rad / Math.PI * 180
+ }
+
+ function toRad (deg) {
+ return deg / 180 * Math.PI
+ }
+
+ function buildRotationTranslation (obj) {
+
+ // used when rotationOrigin is set
+
+ var origin = obj.rotationOrigin
+ if (!origin) {
+ return
+ } else {
+ var dx = origin.x - obj.x,
+ dy = -(origin.y - obj.y),
+ dz = -(origin.z - obj.z)
+ return {
+ before: 'translate3d(' + dx.toFixed(floatPrecision) +'px,' + dy.toFixed(floatPrecision) + 'px,' + dz.toFixed(floatPrecision) + 'px) ',
+ after: 'translate3d(' + (-dx).toFixed(floatPrecision) + 'px,' + (-dy).toFixed(floatPrecision) + 'px,' + (-dz).toFixed(floatPrecision) + 'px) '
+ }
+ }
+ }
+
+ function addPrefix (string) {
+ if (MX.prefix) {
+ string = MX.prefix + string.charAt(0).toUpperCase() + string.slice(1)
+ }
+ return string
+ }
+
+ // ========================================================================
+ // Base Object3D
+ // ========================================================================
+
+ function Object3D (el) {
+
+ this.setupDomElement(el)
+ this.setCSSTransformStyle('preserve-3d')
+ this.el.classList.add('mx-object3d')
+
+ this.parent = undefined
+ this.children = []
+ this.updateChildren = true
+
+ this.inverseLookAt = false
+
+ this.persisted = true
+
+ this.reset()
+
+// this.quaternion = new MX.Quaternion ()
+// this.quaternion._euler = this
+
+ var width, height,
+ self = this
+
+ Object.defineProperty(this, 'width', {
+ get: function () {
+ return width
+ || parseInt(self.el.style.width, 10) * app_devicePixelRatio
+ || 0
+ },
+ set: function (val) {
+ width = val
+ this.el.style.width = (width/app_devicePixelRatio) + 'px'
+ }
+ })
+
+ Object.defineProperty(this, 'height', {
+ get: function () {
+ return height
+ || parseInt(self.el.style.height, 10) * app_devicePixelRatio
+ || 0
+ },
+ set: function (val) {
+ height = val
+ this.el.style.height = (height/app_devicePixelRatio) + 'px'
+ }
+ })
+ }
+
+ Object3D.prototype = {
+
+ constructor: Object3D,
+
+ reset: function () {
+ this.x = this.__x = 0
+ this.y = this.__y = 0
+ this.z = this.__z = 0
+ this.rotationX = this.__rotationX = 0
+ this.rotationY = this.__rotationY = 0
+ this.rotationZ = this.__rotationZ = 0
+ this.scaleX = this.__scaleX = 1
+ this.scaleY = this.__scaleY = 1
+ this.scaleZ = this.__scaleZ = 1
+ this.skewX = this.__skewX = 0
+ this.skewY = this.__skewY = 0
+ this.scale = this.__scale = 1
+ this.perspective = this.__perspective = 0
+ this.rotationOrigin = undefined
+ this.followTarget = undefined
+// this.quaternion = new MX.Quaternion()
+ this.dirty = true
+ this.update()
+ },
+
+ setupDomElement: function (el) {
+ this.el = undefined
+ if (el instanceof HTMLElement) {
+ this.el = el
+ } else if (typeof el === 'string') {
+ var tag = el.match(/^[^.#\s]*/)[1],
+ id = el.match(/#[^.#\s]*/),
+ classes = el.match(/\.[^.#\s]*/g)
+ this.el = document.createElement(tag || 'div')
+ if (id) {
+ this.el.id = id[0].slice(1)
+ }
+ if (classes) {
+ var i = classes.length
+ while (i--) {
+ this.el.classList.add(classes[i].slice(1))
+ }
+ }
+ } else {
+ this.el = document.createElement('div')
+ }
+ },
+
+ update: function () {
+
+ if (this.updateChildren) {
+ var i = this.children.length
+ while (i--) {
+ this.children[i].update()
+ }
+ }
+
+ if (this.followTarget) {
+ this.lookAt(this.followTarget, false)
+ }
+
+ if (this.scaleX !== this.__scaleX ||
+ this.scaleY !== this.__scaleY ||
+ this.scaleZ !== this.__scaleZ) {
+ this.__scaleX = this.scaleX
+ this.__scaleY = this.scaleY
+ this.__scaleZ = this.scaleZ
+ this.dirty = true
+ }
+
+ if (this.skewX !== this.__skewX ||
+ this.skewY !== this.__skewY) {
+ this.__skewX = this.skewX
+ this.__skewY = this.skewY
+ this.dirty = true
+ }
+
+ if (this.scale !== this.__scale) {
+ this.scaleX =
+ this.scaleY =
+ this.scaleZ =
+ this.__scaleX =
+ this.__scaleY =
+ this.__scaleZ =
+ this.__scale =
+ this.scale
+ this.dirty = true
+ }
+
+ if (this.rotationX !== this.__rotationX ||
+ this.rotationY !== this.__rotationY ||
+ this.rotationZ !== this.__rotationZ) {
+ this.__rotationX = this.rotationX
+ this.__rotationY = this.rotationY
+ this.__rotationZ = this.rotationZ
+ this.dirty = true
+ }
+
+ if (this.x !== this.__x ||
+ this.y !== this.__y ||
+ this.z !== this.__z) {
+ this.__x = this.x
+ this.__y = this.y
+ this.__z = this.z
+ this.dirty = true
+ }
+
+ if (this.perspective !== this.__perspective) {
+ this.__perspective = this.perspective
+ this.dirty = true
+ }
+
+ if (this.dirty && this.el) {
+
+ var rotationTranslation = buildRotationTranslation(this),
+ rotation = 'rotateX(' + this.rotationX.toFixed(floatPrecision) + MX.rotationUnit + ') '
+ + 'rotateY(' + this.rotationY.toFixed(floatPrecision) + MX.rotationUnit + ') '
+ + 'rotateZ(' + this.rotationZ.toFixed(floatPrecision) + MX.rotationUnit + ') '
+
+ var transformString =
+ (MX.positionAtCenter ? 'translate3d(-50%, -50%, 0) ' : '')
+ + (this.perspective ? 'perspective(' + this.perspective + 'px) ' : '')
+ + 'translate3d('
+ + this.x.toFixed(floatPrecision || 0) + 'px,'
+ + (-this.y).toFixed(floatPrecision) + 'px,'
+ + (-this.z).toFixed(floatPrecision) + 'px) '
+ + 'scale3d('
+ + (app_devicePixelRatio * this.scaleX).toFixed(floatPrecision) + ','
+ + (app_devicePixelRatio * this.scaleY).toFixed(floatPrecision) + ','
+ + (app_devicePixelRatio * this.scaleZ).toFixed(floatPrecision) + ') '
+ + 'skew('
+ + (app_devicePixelRatio * this.skewX).toFixed(floatPrecision) + 'rad,'
+ + (app_devicePixelRatio * this.skewY).toFixed(floatPrecision) + 'rad) '
+
+ if (rotationTranslation) {
+ transformString += rotationTranslation.before
+ + rotation
+ + rotationTranslation.after
+
+ } else {
+ transformString += rotation
+ }
+
+ this.el.style[transformProp] = transformString
+ this.dirty = false
+ }
+
+ return this
+
+ },
+
+ // taken from three.js
+ setFromQuaternion: function ( q, order, update ) {
+ // q is assumed to be normalized
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
+
+ var sqx = q.x * q.x;
+ var sqy = q.y * q.y;
+ var sqz = q.z * q.z;
+ var sqw = q.w * q.w;
+
+ this.rotationX = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
+ this.rotationY = Math.asin( clamp( 2 * ( q.x * q.z + q.y * q.w ), -1, 1 ) );
+ this.rotationZ = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
+ },
+
+ lookAt: function (target, update) {
+ var r = this.getLookAtEuler(target)
+ this.setRotation(r)
+ if (update !== false) this.update()
+ return this
+ },
+
+ getLookAtEuler: function (target) {
+ // euler order XYZ
+ var r = {},
+ dx = target.x - this.x,
+ dy = target.y - this.y,
+ dz = target.z - this.z
+ if (this.inverseLookAt) {
+ dx = -dx
+ dy = -dy
+ dz = -dz
+ }
+ if (dz === 0) dz = 0.001
+ r.x = -Math.atan2(dy, dz)
+ var flip = dz > 0 ? 1 : -1
+ r.y = flip * Math.atan2(dx * Math.cos(r.x), dz * -flip)
+ r.z = Math.atan2(Math.cos(r.x), Math.sin(r.x) * Math.sin(r.y)) - Math.PI / 2
+ if (MX.rotationUnit === 'deg') {
+ r.x = toDeg(r.x)
+ r.y = toDeg(r.y)
+ r.z = toDeg(r.z)
+ }
+ return r
+ },
+
+ add: function () {
+ if (!this.el) return
+ var parent = this
+ Array.prototype.forEach.call(arguments, function (child) {
+ if (!child instanceof Object3D) return
+ parent.el.appendChild(child.el)
+ if (!parent.children) parent.children = []
+ parent.children.push(child)
+ child.parent = parent
+ })
+ return this
+ },
+
+ remove: function () {
+ var parent = this
+ Array.prototype.forEach.call(arguments, function (child) {
+ var index = parent.children.indexOf(child)
+ if (index !== -1) {
+ parent.children.splice(index, 1)
+ parent.el.removeChild(child.el)
+ child.parent = undefined
+ }
+ })
+ return this
+ },
+
+ addTo: function (target) {
+ if (typeof target === 'string') {
+ target = document.querySelector(target)
+ }
+ if (target instanceof HTMLElement && target.appendChild) {
+ target.appendChild(this.el)
+ } else if (target instanceof Object3D || target instanceof MX.Scene) {
+ target.add(this)
+ }
+ return this
+ },
+
+ removeElement: function () {
+ if (this.el.parentNode) {
+ this.el.parentNode.removeChild(this.el)
+ }
+ },
+
+ setPosition: function (tar) {
+ this.x = (tar.x || tar.x === 0) ? tar.x : this.x
+ this.y = (tar.y || tar.y === 0) ? tar.y : this.y
+ this.z = (tar.z || tar.z === 0) ? tar.z : this.z
+ },
+
+ setRotation: function (tar) {
+ this.rotationX = (tar.x || tar.x === 0) ? tar.x : this.rotationX
+ this.rotationY = (tar.y || tar.y === 0) ? tar.y : this.rotationY
+ this.rotationZ = (tar.z || tar.z === 0) ? tar.z : this.rotationZ
+ },
+
+ setScale: function (tar) {
+ this.scaleX = (tar.x || tar.x === 0) ? tar.x : this.scaleX
+ this.scaleY = (tar.y || tar.y === 0) ? tar.y : this.scaleY
+ this.scaleZ = (tar.z || tar.z === 0) ? tar.z : this.scaleZ
+ },
+
+ setSkew: function (tar) {
+ this.skewX = (tar.x || tar.x === 0) ? tar.x : this.skewX
+ this.skewY = (tar.y || tar.y === 0) ? tar.y : this.skewY
+ },
+
+ setCSSTransformOrigin: function (origin) {
+ this.el && (this.el.style[transformOriginProp] = origin)
+ return this
+ },
+
+ setCSSTransformStyle: function (style) {
+ this.el && (this.el.style[transformStyleProp] = style)
+ return this
+ },
+
+ setCSSTransition: function (trans) {
+ this.el && (this.el.style[transitionProp] = trans)
+ return this
+ },
+
+ setCSSPerspective: function (pers) {
+ this.el && (this.el.style[perspectiveProp] = pers)
+ return this
+ },
+
+ move: function(ops){
+ var layer = this
+ layer.ops = defaults(ops, layer.ops)
+ for (var i in ops) {
+ layer[i] = ops[i]
+ }
+ layer.dirty = true
+ layer.update()
+ },
+
+ onTransitionEnd: function (callback) {
+ this.cancelTransitionEnd()
+ var el = this.el
+ el.addEventListener(transitionEndEvent, onEnd)
+ function onEnd () {
+ el.removeEventListener(transitionEndEvent, onEnd)
+ callback()
+ }
+ },
+
+ cancelTransitionEnd: function () {
+ this.el.removeEventListener(transitionEndEvent)
+ },
+
+ toString: function(params){
+ params = params || "id width height depth x y z rotationX rotationY rotationZ scale".split(" ")
+ return this.__toString(params)
+ },
+
+ __toString: function(params, func){
+ this.id = this.id || 'undef' // _.uniqueId()
+ var list = [],
+ obj = {},
+ type = this.type || "Object3d",
+ name = type.toLowerCase(),
+ val,
+ param
+ for (var i in params) {
+ param = params[i]
+ val = this[param]
+ if (val === 0 && ! func) continue;
+ if (typeof val == "number") {
+ if (param.indexOf("rotation") != -1) {
+ obj[param] = Number(val.toFixed(3))
+ }
+ else {
+ obj[param] = ~~val
+ }
+ }
+ else {
+ obj[param] = val
+ }
+ }
+
+ return (func || "var " + name + " = new MX." + type ) + "(" +
+ JSON.stringify(obj, undefined, 2) +
+ ")\n" + (func ? "" : "scene.add(" + name + ")")
+ },
+
+ contains: function(x,y,z){
+ var containsX = false,
+ containsY = false,
+ containsZ = false
+
+ if (x === null) {
+ containsX = true
+ }
+ else {
+ containsX = abs(this.x - x) <= this.width/2
+ }
+
+ if (y === null) {
+ containsY = true
+ }
+ else {
+ containsY = abs(this.y - y) <= this.height/2
+ }
+
+ if (z === null) {
+ containsZ = true
+ }
+ else {
+ containsZ = abs(this.z - z) <= this.depth/2
+ }
+
+ return (containsX && containsY && containsZ)
+ }
+
+ }
+
+ // ========================================================================
+ // Inheritance
+ // ========================================================================
+
+ Object3D.extend = extend.bind(Object3D)
+
+ function extend (props) {
+ var Super = this
+ var ExtendedObject3D = function () {
+ Super.call(this)
+ props.init && props.init.apply(this, arguments)
+ }
+ ExtendedObject3D.prototype = Object.create(Super.prototype)
+ for (var prop in props) {
+ if (props.hasOwnProperty(prop) && prop !== 'init') {
+ ExtendedObject3D.prototype[prop] = props[prop]
+ }
+ }
+ ExtendedObject3D.extend = extend.bind(ExtendedObject3D)
+ return ExtendedObject3D
+ }
+
+ // ========================================================================
+ // Expose API
+ // ========================================================================
+
+ MX.Object3D = Object3D
+ MX.toRad = toRad
+ MX.toDeg = toDeg
+
+ // center positioning getter setter
+ Object.defineProperty(MX, 'positionAtCenter', {
+ get: function () {
+ return positionAtCenter
+ },
+ set: function (val) {
+ if (typeof val !== 'boolean') return
+ positionAtCenter = val
+ if (positionAtCenter) {
+ injectCenteringCSS()
+ } else {
+ removeCenteringCSS()
+ }
+ }
+ })
+
+ return MX
+
+})() \ No newline at end of file
diff --git a/assets/javascripts/mx/primitives/mx.image.js b/assets/javascripts/mx/primitives/mx.image.js
new file mode 100644
index 0000000..39bb0b5
--- /dev/null
+++ b/assets/javascripts/mx/primitives/mx.image.js
@@ -0,0 +1,50 @@
+MX.Image = MX.Object3D.extend({
+ init: function (ops) {
+
+ this.type = "Image"
+ this.media = ops.media
+ this.width = 0
+ this.height = 0
+ this.x = ops.x || 0
+ this.y = ops.y || 0
+ this.z = ops.z || 0
+ this.scale = ops.scale || 1
+ this.backface = ops.backface || false
+
+ ops.className && this.el.classList.add(ops.className)
+ this.backface && this.el.classList.add("backface-visible")
+ this.el.classList.add("image")
+ this.el.classList.add("mx-scenery")
+
+ this.el.style.backgroundRepeat = 'no-repeat'
+
+ this.load(ops)
+ },
+
+ load: function(ops){
+ var layer = this
+ layer.ops = defaults(ops, layer.ops)
+
+ var image = new Image()
+ image.onload = function(){
+ if (! layer.ops) return
+ layer.scale = layer.ops.scale || 1
+ layer.width = layer.ops.width || image.naturalWidth
+ layer.height = layer.ops.height || image.naturalHeight
+// layer.x = layer.ops.x || 0
+// layer.y = layer.ops.y || 0
+// layer.z = layer.ops.z || 0
+// layer.rotationX = layer.ops.rotationX || 0
+// layer.rotationY = layer.ops.rotationY || 0
+// layer.rotationZ = layer.ops.rotationZ || 0
+ layer.el.style.backgroundImage = "url(" + image.src + ")"
+ layer.el.classList.add('image')
+ layer.dirty = true
+ layer.ops.onload && layer.ops.onload( image )
+ layer.update()
+ }
+ image.src = ops.src;
+ if (image.complete) setTimeout(image.onload)
+ },
+
+})