diff options
| author | Julie Lala <jules@okfoc.us> | 2014-04-17 10:17:09 -0400 |
|---|---|---|
| committer | Julie Lala <jules@okfoc.us> | 2014-04-17 10:17:09 -0400 |
| commit | 6005be1d04dd02000889d8be2faaf62969c4421f (patch) | |
| tree | 12c2151b606188bd02b656689928035c0561bf36 /assets/javascripts/mx | |
stowing UI stuff in empty branchui-mocks
Diffstat (limited to 'assets/javascripts/mx')
| -rw-r--r-- | assets/javascripts/mx/extensions/mx.movements.js | 160 | ||||
| -rw-r--r-- | assets/javascripts/mx/extensions/mx.rotationControl.js | 266 | ||||
| -rw-r--r-- | assets/javascripts/mx/extensions/mx.scene.js | 161 | ||||
| -rw-r--r-- | assets/javascripts/mx/mx.js | 497 | ||||
| -rw-r--r-- | assets/javascripts/mx/mx.min.js | 1 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.box.js | 62 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.boxDimensions.js | 88 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.coords.js | 61 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.door.js | 75 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.face.js | 41 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.iframe.js | 19 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.image.js | 40 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.texturedBox.js | 119 | ||||
| -rw-r--r-- | assets/javascripts/mx/primitives/mx.video.js | 48 |
14 files changed, 1638 insertions, 0 deletions
diff --git a/assets/javascripts/mx/extensions/mx.movements.js b/assets/javascripts/mx/extensions/mx.movements.js new file mode 100644 index 0000000..691ada7 --- /dev/null +++ b/assets/javascripts/mx/extensions/mx.movements.js @@ -0,0 +1,160 @@ + + +MX.Movements = function (cam, viewHeight, minimap) { + + var moveForward, + moveLeft, + moveBackward, + moveRight, + turnLeft, + turnRight, + jumping = false + + var v = 25, + vr = Math.PI * 0.015 + jumpV = 30, + vx = vy = vz = 0 + + return { + + init: function () { + + document.addEventListener('keydown', function (e) { + $(".edit-image.menu,.edit-video.menu").hide() + + switch ( e.keyCode ) { + case 38: // up + case 87: // w + moveForward = true + break + + case 37: // left + case 65: // a + turnLeft = true + break + + case 40: // down + case 83: // s + moveBackward = true + break + + case 39: // right + case 68: // d + turnRight = true + break + + case 32: // space + if (!jumping) vy += jumpV + jumping = true + break + } + }) + + document.addEventListener('keyup', function (e) { + $(".edit-image.menu,.edit-video.menu").hide() + + switch ( e.keyCode ) { + case 38: // up + case 87: // w + moveForward = false + break + + case 37: // left + case 65: // a + turnLeft = false + break + + case 40: // down + case 83: // s + moveBackward = false + break + + case 39: // right + case 68: // d + turnRight = false + break + } + }) + + var mouseX, mouseY, dx, dy, rotX, rotY, dragging = false + document.addEventListener('mousedown', function (e) { + $(".edit-image.menu,.edit-video.menu").hide() + + mouseX = e.pageX + mouseY = e.pageY + rotX = cam.rotationX + rotY = cam.rotationY + dragging = true + }) + document.addEventListener('mousemove', function (e) { + if (! dragging || app.dragging) return + var dx = (e.pageX - mouseX) / window.innerWidth * Math.PI/3 + var dy = (e.pageY - mouseY) / window.innerHeight * Math.PI/3 + cam.rotationY = rotY + dx + cam.rotationX = rotX - dy + minimap.update() + }) + document.addEventListener('mouseup', function (e) { + app.dragging = dragging = false + }) + + window.addEventListener('blur', function(e){ + $(".edit-image.menu,.edit-video.menu").hide() + moveForward = moveLeft= moveBackward = moveRight = turnLeft = turnRight = jumping = dragging = false + }) + + }, + + update: function () { + + var ry = cam.rotationY + + if (moveForward || moveBackward || moveRight || moveLeft || turnLeft || turnRight) { + + vx = vz = 0 + + if (moveForward) { + vx += v * Math.cos(ry + Math.PI / 2) + vz += v * Math.sin(ry + Math.PI / 2) + } + if (moveBackward) { + vx -= v * Math.cos(ry + Math.PI / 2) + vz -= v * Math.sin(ry + Math.PI / 2) + } + if (moveLeft) { + vx -= v * Math.cos(ry) + vz -= v * Math.sin(ry) + } + if (moveRight) { + vx += v * Math.cos(ry) + vz += v * Math.sin(ry) + } + + if (turnLeft) { + cam.rotationY += vr + } + if (turnRight) { + cam.rotationY -= vr + } + + cam.x += vx + cam.z += vz + + minimap.update() + + } + + vy -= 1 + + // update cam + cam.y += vy + + if (cam.y <= viewHeight) { + cam.y = viewHeight + vy = 0 + jumping = false + } + + } + } +} diff --git a/assets/javascripts/mx/extensions/mx.rotationControl.js b/assets/javascripts/mx/extensions/mx.rotationControl.js new file mode 100644 index 0000000..3bdc043 --- /dev/null +++ b/assets/javascripts/mx/extensions/mx.rotationControl.js @@ -0,0 +1,266 @@ +// Usage: +// +// var control = new MX.RotationControl() +// control.init( object{MX.Object3D} [, listener{HTMLElement}] ) +// +// In animation loop: +// +// control.update() +// +// The above code will register handler functions on `listener` +// and will be updating `object`s rotationX and rotationY +// If no `listener` is provided, will default to `object`s el. + +MX.RotationControl = function () { + + var object, + locked = false + + var down = false, + active = false, + lastX, + lastY + + var pointerLockPrefix = + 'pointerLockElement' in document ? '' : + 'mozPointerLockElement' in document ? 'moz' : + 'webkitPointerLockElement' in document ? 'webkit' : null, + hasPointerLock = !(pointerLockPrefix === null) + pointerLockEnabled = false + + var pub = { + + sensitivity : .5, + ease : 10, + drag : true, + + inverseX : false, + inverseY : false, + + disableX : false, + disableY : false, + + rotationX : 0, + rotationY : 0, + + upperBoundX : undefined, + lowerBoundX : undefined, + + upperBoundY : undefined, + lowerBoundY : undefined, + + usePreset: function (name) { + var ops = presets[name] + if (ops) { + if (currentPreset && presets[currentPreset].teardown) { + presets[currentPreset].teardown() + } + for (var op in ops) { + if (op !== 'setup' && op !== 'teardown') { + pub[op] = ops[op] + } + } + if (op.setup) ops.setup() + } + } + } + + var currentPreset + var presets = { + firstPerson: { + drag: false, + ease: 2, + sensitivity: .18, + inverseX: true, + inverseY: true, + upperBoundX: MX.rotationUnit === 'deg' ? 90 : Math.PI / 2, + lowerBoundX: MX.rotationUnit === 'deg' ? -90 : -Math.PI / 2 + }, + skybox: { + sensitivity: .18, + inverseX: true, + inverseY: true, + upperBoundX: MX.rotationUnit === 'deg' ? 90 : Math.PI / 2, + lowerBoundX: MX.rotationUnit === 'deg' ? -90 : -Math.PI / 2 + } + } + + function init (obj, lis) { + if (active) return + + object = obj + pub.rotationX = object.rotationX + pub.rotationY = object.rotationY + + if (lis instanceof HTMLElement) { + listener = lis + } else if (lis instanceof MX.Object3D) { + listener = lis.el + } else { + listener = window.document + } + + listener.addEventListener('mousedown', onDown) + listener.addEventListener('mousemove', onMove) + listener.addEventListener('mouseup', onUp) + listener.addEventListener('touchstart', onDown) + listener.addEventListener('touchmove', onMove) + listener.addEventListener('touchend', onUp) + + active = true + } + + function changeObject (obj) { + object = obj + pub.rotationX = object.rotationX + pub.rotationY = object.rotationY + } + + function changeListener (lis) { + remove() + active = false + init(object, lis) + if (pointerLockEnabled) { + initPointerLock() + } + } + + function remove () { + if (!active) return + listener.removeEventListener('mousedown', onDown) + listener.removeEventListener('mousemove', onMove) + listener.removeEventListener('mouseup', onUp) + listener.removeEventListener('touchstart', onDown) + listener.removeEventListener('touchmove', onMove) + listener.removeEventListener('touchend', onUp) + + if (hasPointerLock) { + document.removeEventListener(pointerLockPrefix + 'pointerlockchange', onPointerLockChange) + document.removeEventListener('mousemove', onPointerLockMove) + document.body[pointerLockPrefix + (pointerLockPrefix ? 'E' : 'e') + 'xitPointerLock']() + } + active = false + } + + function onDown (e) { + e = normalizeEvent(e) + if (!e) return + down = true + lastX = e.pageX + lastY = e.pageY + } + + function onMove (e) { + if (app.dragging) return; + if (e.type = 'touchmove') { + e.preventDefault() + } + if (pub.drag && !down) return + e = normalizeEvent(e) + if (!e) return + lastX = lastX || e.pageX + lastY = lastY || e.pageY + var dx = e.pageX - lastX, + dy = e.pageY - lastY + lastX = e.pageX + lastY = e.pageY + updateTarget(dx, dy) + } + + function onUp () { + app.dragging = down = false + } + + function initPointerLock () { + + if (pointerLockEnabled) return + + document.addEventListener(pointerLockPrefix + 'pointerlockchange', onPointerLockChange) + document.addEventListener('mousemove', onPointerLockMove) + + document.body[pointerLockPrefix + (pointerLockPrefix ? 'R' : 'r') + 'equestPointerLock']() + } + + function onPointerLockChange () { + var el = document.body + if (document[pointerLockPrefix + (pointerLockPrefix ? 'P' : 'p') + 'ointerLockElement'] === el) { + pointerLockEnabled = true + } else { + pointerLockEnabled = false + } + } + + function onPointerLockMove (e) { + if (!pointerLockEnabled) return + var dx = e[pointerLockPrefix + (pointerLockPrefix ? 'M' : 'm') + 'ovementX'], + dy = e[pointerLockPrefix + (pointerLockPrefix ? 'M' : 'm') + 'ovementY'] + updateTarget(dx, dy) + } + + function normalizeEvent (e) { + if (e.touches) { + return e.touches.length > 1 ? false : e.touches[0] + } else { + return e + } + } + + function updateTarget (dx, dy) { + if (pub.inverseX) dx = -dx + if (pub.inverseY) dy = -dy + if (MX.rotationUnit !== 'deg') { + dx = MX.toRad(dx) + dy = MX.toRad(dy) + } + + if (!pub.disableX) { + pub.rotationX -= dy * pub.sensitivity + if (pub.upperBoundX) pub.rotationX = Math.min(pub.rotationX, pub.upperBoundX) + if (pub.lowerBoundX) pub.rotationX = Math.max(pub.rotationX, pub.lowerBoundX) + } + + if (!pub.disableY) { + pub.rotationY += dx * pub.sensitivity + if (pub.upperBoundY) pub.rotationY = Math.min(pub.rotationY, pub.upperBoundY) + if (pub.lowerBoundY) pub.rotationY = Math.max(pub.rotationY, pub.lowerBoundY) + } + } + + function update () { + if (!object || locked) return + var dx = pub.rotationX - object.rotationX, + dy = pub.rotationY - object.rotationY + if (Math.abs(dx) < 0.0001) { + object.rotationX = pub.rotationX + } else { + object.rotationX += dx / pub.ease + } + if (Math.abs(dy) < 0.0001) { + object.rotationY = pub.rotationY + } else { + object.rotationY += dy / pub.ease + } + } + + function lock () { + locked = true + } + + function unlock () { + pub.rotationX = object.rotationX + pub.rotationY = object.rotationY + locked = false + } + + pub.init = init + pub.remove = remove + pub.update = update + pub.lock = lock + pub.unlock = unlock + pub.initPointerLock = initPointerLock + pub.changeObject = changeObject + pub.changeListener = changeListener + + return pub + +}
\ No newline at end of file diff --git a/assets/javascripts/mx/extensions/mx.scene.js b/assets/javascripts/mx/extensions/mx.scene.js new file mode 100644 index 0000000..c1501f5 --- /dev/null +++ b/assets/javascripts/mx/extensions/mx.scene.js @@ -0,0 +1,161 @@ +// 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.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.Object3D() + cam.el = null + + // cam's lookAt is a bit different + // ignoring rotationZ + cam.getLookAtEuler = getCameraEuler.bind(cam) + + 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 + }, + + sizeToScreen: function(){ + scene.width = window.innerWidth + scene.height = window.innerHeight + if (is_mobile) { + scene.perspective = min(window.innerWidth, window.innerHeight) - 80 + } + else { + scene.perspective = min(window.innerWidth, window.innerHeight) + } + }, + + } + + function getCameraEuler (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 + } + + return Scene + +})()
\ No newline at end of file diff --git a/assets/javascripts/mx/mx.js b/assets/javascripts/mx/mx.js new file mode 100644 index 0000000..a96274b --- /dev/null +++ b/assets/javascripts/mx/mx.js @@ -0,0 +1,497 @@ +/** + * 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() + + app.init() + 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.reset() + + var width, height, + self = this + + Object.defineProperty(this, 'width', { + get: function () { + return width + || parseInt(self.el.style.width, 10) + || 0 + }, + set: function (val) { + width = val + this.el.style.width = width + 'px' + } + }) + + Object.defineProperty(this, 'height', { + get: function () { + return height + || parseInt(self.el.style.height, 10) + || 0 + }, + set: function (val) { + height = val + this.el.style.height = height + '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.scale = this.__scale = 1 + this.perspective = this.__perspective = 0 + this.rotationOrigin = undefined + this.followTarget = undefined + 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.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) + 'px,' + + (-this.y).toFixed(floatPrecision) + 'px,' + + (-this.z).toFixed(floatPrecision) + 'px) ' + + 'scale3d(' + + this.scaleX.toFixed(floatPrecision) + ',' + + this.scaleY.toFixed(floatPrecision) + ',' + + this.scaleZ.toFixed(floatPrecision) + ') ' + + if (rotationTranslation) { + transformString += rotationTranslation.before + + rotation + + rotationTranslation.after + + } else { + transformString += rotation + } + + this.el.style[transformProp] = transformString + this.dirty = false + } + + return this + + }, + + 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) + 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 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 + }, + + 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 + }, + + 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) + } + + } + + // ======================================================================== + // 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/mx.min.js b/assets/javascripts/mx/mx.min.js new file mode 100644 index 0000000..b0f0cdd --- /dev/null +++ b/assets/javascripts/mx/mx.min.js @@ -0,0 +1 @@ +var MX=MX||function(undefined){var MX={prefix:undefined,rotationUnit:"rad"};var floatPrecision=5;var transformProp,transitionProp,transformOriginProp,transformStyleProp,perspectiveProp;var positionAtCenter=true,centeringCSS;document.addEventListener("DOMContentLoaded",setup);function setup(){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");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"]}centeringCSS=document.createElement("style");centeringCSS.type="text/css";centeringCSS.innerHTML=".mx-object3d {"+"position: absolute;"+"top: 50%;"+"left: 50%;}";injectCenteringCSS()}function injectCenteringCSS(){document.head.appendChild(centeringCSS)}function removeCenteringCSS(){document.head.removeChild(centeringCSS)}function toDeg(rad){return rad/Math.PI*180}function toRad(deg){return deg/180*Math.PI}function buildRotationTranslation(obj){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+"px,"+dy+"px,"+dz+"px) ",after:"translate3d("+-dx+"px,"+-dy+"px,"+-dz+"px) "}}}function addPrefix(string){if(MX.prefix){string=MX.prefix+string.charAt(0).toUpperCase()+string.slice(1)}return string}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.reset();var width,height,self=this;Object.defineProperty(this,"width",{get:function(){return width||parseInt(self.el.style.width,10)||0},set:function(val){width=val;this.el.style.width=width+"px"}});Object.defineProperty(this,"height",{get:function(){return height||parseInt(self.el.style.height,10)||0},set:function(val){height=val;this.el.style.height=height+"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.scale=this.__scale=1;this.rotationOrigin=undefined;this.followTarget=undefined;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.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.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) ":"")+"translate3d("+this.x.toFixed(floatPrecision)+"px,"+(-this.y).toFixed(floatPrecision)+"px,"+(-this.z).toFixed(floatPrecision)+"px) "+"scale3d("+this.scaleX.toFixed(floatPrecision)+","+this.scaleY.toFixed(floatPrecision)+","+this.scaleZ.toFixed(floatPrecision)+") ";if(rotationTranslation){transformString+=rotationTranslation.before+rotation+rotationTranslation.after}else{transformString+=rotation}this.el.style[transformProp]=transformString;this.dirty=false}return this},lookAt:function(target,update){var r=this.getLookAtEuler(target);this.setRotation(r);if(update!==false)this.update();return this},getLookAtEuler:function(target){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=.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);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 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},setCSSTransformOrigin:function(origin){this.el&&(this.el.style[transformOriginProp]=addPrefix(origin));return this},setCSSTransformStyle:function(style){this.el&&(this.el.style[transformStyleProp]=addPrefix(style));return this},setCSSTransition:function(trans){this.el&&(this.el.style[transitionProp]=addPrefix(trans));return this},setCSSPerspective:function(pers){this.el&&(this.el.style[perspectiveProp]=addPrefix(pers));return this}};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}MX.Object3D=Object3D;MX.toRad=toRad;MX.toDeg=toDeg;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.box.js b/assets/javascripts/mx/primitives/mx.box.js new file mode 100644 index 0000000..9f053da --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.box.js @@ -0,0 +1,62 @@ +MX.Box = MX.Object3D.extend({ + + // this will be called within the contructor + init: function (size, color, borderColor) { + + this.type = "Box" + + size = size || 100 + color = color || 'rgba(0, 255, 122, .1)' + borderColor = borderColor || '#0f3' + + // an Object3D's associated DOM node is the "el" property + this.el.classList.add('box') + + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2) + + var top = this.top = new MX.Object3D('.face') + top.rotationX = angle + top.y = size / 2 + + var bottom = this.bottom = new MX.Object3D('.face') + bottom.rotationX = -angle + bottom.y = -size / 2 + + var left = this.left = new MX.Object3D('.face') + left.rotationY = -angle + left.x = -size / 2 + + var right = this.right = new MX.Object3D('.face') + right.rotationY = angle + right.x = size / 2 + + var front = this.front = new MX.Object3D('.face') + front.z = -size / 2 + + var back = this.back = new MX.Object3D('.face') + back.rotationY = angle * 2 + back.z = size / 2 + + // adding children, must also be instances of Object3D + this.add(top, bottom, left, right, front, back) + + this.children.forEach(function (face) { + face.width = size - 2 + face.height = size - 2 + face.el.style.backgroundColor = color + face.el.style.border = '1px solid ' + borderColor + }) + + // this applies the updated CSS style + // required for any change to take effect + // when a parent object's update() is called + // all its children will be updated as well + this.update() + + // if this object's children won't move by themselves + this.updateChildren = false + } + + // other properties will be mixed into the prototype of the new constructor + +})
\ No newline at end of file diff --git a/assets/javascripts/mx/primitives/mx.boxDimensions.js b/assets/javascripts/mx/primitives/mx.boxDimensions.js new file mode 100644 index 0000000..d1d507d --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.boxDimensions.js @@ -0,0 +1,88 @@ +MX.BoxDimensions = MX.Object3D.extend({ + + // this will be called within the contructor + init: function (opt) { + + this.type = "BoxDimensions" + + this.opt = opt + + var width = opt.width || 100 + var height = opt.height || 100 + var depth = opt.depth || 100 + var color = this.color = opt.color || 'rgba(0, 255, 122, .1)' + var borderColor = this.borderColor = opt.borderColor || '#0f3' + var sides = this.sides = opt.sides || "top bottom left right back" + + // an Object3D's associated DOM node is the "el" property + this.el.classList.add('box') + + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2) + + var top = this.top = new MX.Object3D('.face.top') + top.rotationX = angle + top.width = width + top.height = depth + top.y = height + + var bottom = this.bottom = new MX.Object3D('.face.bottom') + bottom.rotationX = -angle + bottom.width = width + bottom.height = depth + bottom.y = 0 + + var left = this.left = new MX.Object3D('.face.left') + left.rotationY = -angle + left.width = depth + left.height = height + left.x = -width/2 + left.y = height/2 + + var right = this.right = new MX.Object3D('.face.right') + right.rotationY = angle + right.width = depth + right.height = height + right.x = width/2 + right.y = height/2 + + var front = this.front = new MX.Object3D('.face.front') + front.width = width + front.height = height + front.z = -depth/2 + front.y = height/2 + + var back = this.back = new MX.Object3D('.face.back') + back.width = width + back.height = height + back.rotationY = angle * 2 + back.z = depth/2 + back.y = height/2 + + // adding children, must also be instances of Object3D + if (-1 != sides.indexOf("top")) this.add(top) + if (-1 != sides.indexOf("bottom")) this.add(bottom) + if (-1 != sides.indexOf("left")) this.add(left) + if (-1 != sides.indexOf("right")) this.add(right) + if (-1 != sides.indexOf("front")) this.add(front) + if (-1 != sides.indexOf("back")) this.add(back) + + this.children.forEach(function (face) { + face.el.style.backgroundColor = color + face.el.style.border = '3px solid ' + borderColor + }) + + bottom.el.style.border = "0" + + // this applies the updated CSS style + // required for any change to take effect + // when a parent object's update() is called + // all its children will be updated as well + this.update() + + // if this object's children won't move by themselves + this.updateChildren = false + } + + // other properties will be mixed into the prototype of the new constructor + +})
\ No newline at end of file diff --git a/assets/javascripts/mx/primitives/mx.coords.js b/assets/javascripts/mx/primitives/mx.coords.js new file mode 100644 index 0000000..80b148c --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.coords.js @@ -0,0 +1,61 @@ +MX.Coords = (function () { + + var colors = { + x: '#f33', + y: '#3f3', + z: '#66f' + } + + var Axis = MX.Object3D.extend({ + init: function (axis, size) { + + var label = document.createElement('span') + label.textContent = axis.toUpperCase() + label.style.position = 'absolute' + label.style.right = '0px' + label.style.bottom = '3px' + label.style.fontSize = Math.round(size / 10) + 'px' + this.el.appendChild(label) + + var faceA = new MX.Object3D(), + faceB = new MX.Object3D() + faceA.rotationX = 90 + this.add(faceA, faceB) + + this.el.style.color = + faceA.el.style.backgroundColor = + faceB.el.style.backgroundColor = colors[axis] + + this.width = + faceA.width = + faceB.width = size + + this.height = + faceA.height = + faceB.height = Math.round(size / 100) + + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2) + + if (axis === 'y') { + this.rotationZ = -angle + } else if (axis === 'z') { + this.rotationY = angle + } + } + }) + + var Coords = MX.Object3D.extend({ + init: function (size) { + size = size || 100 + var x = new Axis('x', size), + y = new Axis('y', size), + z = new Axis('z', size) + this.add(x, y, z) + this.update() + this.updateChildren = false + } + }) + + return Coords + +})()
\ No newline at end of file diff --git a/assets/javascripts/mx/primitives/mx.door.js b/assets/javascripts/mx/primitives/mx.door.js new file mode 100644 index 0000000..12ff148 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.door.js @@ -0,0 +1,75 @@ + +borderThickness = 3 + +MX.Door = MX.Object3D.extend({ + + // this will be called within the contructor + init: function (opt) { + + width = opt.width || 100 + height = opt.height || 100 + doorOffset = opt.doorOffset || 0 + doorWidth = opt.doorWidth || 30 + doorHeight = opt.doorHeight || 20 + color = opt.color || 'rgba(0, 255, 122, .1)' + borderColor = opt.borderColor || '#0f3' + + // an Object3D's associated DOM node is the "el" property + this.el.classList.add('box') + + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2) + + var left = new MX.Object3D('.face.door.leftTop') + left.width = (width - doorWidth) / 2 + doorOffset + left.height = height-doorHeight-borderThickness + left.x = (width + doorWidth) / 4 + doorOffset + left.y = (height+doorHeight+borderThickness)/2 + left.el.style.backgroundColor = color + left.el.style.borderTop = borderThickness + 'px solid ' + borderColor + left.el.style.borderRight = borderThickness + 'px solid ' + borderColor + this.add(left) + + var leftBot = new MX.Object3D('.face.door.leftBot') + leftBot.width = (width - doorWidth) / 2 - doorOffset + leftBot.height = doorHeight + borderThickness + leftBot.x = (width + doorWidth) / 4 - doorOffset + leftBot.y = (doorHeight+borderThickness)/2 + leftBot.el.style.backgroundColor = color + leftBot.el.style.borderLeft = borderThickness + 'px solid ' + borderColor + leftBot.el.style.borderRight = borderThickness + 'px solid ' + borderColor + leftBot.el.style.borderBottom = borderThickness + 'px solid ' + borderColor + this.add(leftBot) + + var rightTop = new MX.Object3D('.face.door.rightTop') + rightTop.width = (width - doorWidth) / 2 - doorOffset + rightTop.height = height-doorHeight-borderThickness + rightTop.x = -(width+doorWidth)/4 - doorOffset + rightTop.y = (height+ doorHeight+borderThickness)/2 + rightTop.el.style.backgroundColor = color + rightTop.el.style.borderTop = borderThickness + 'px solid ' + borderColor + rightTop.el.style.borderLeft = borderThickness + 'px solid ' + borderColor + this.add(rightTop) + + var rightBot = new MX.Object3D('.face.door.rightBot') + rightBot.width = (width - doorWidth) / 2 - doorOffset + rightBot.height = doorHeight+borderThickness + rightBot.x = -(width + doorWidth)/4 - doorOffset + rightBot.y = (doorHeight+borderThickness)/2 + rightBot.el.style.backgroundColor = color + rightBot.el.style.borderLeft = borderThickness + 'px solid ' + borderColor + rightBot.el.style.borderRight = borderThickness + 'px solid ' + borderColor + rightBot.el.style.borderBottom = borderThickness + 'px solid ' + borderColor + this.add(rightBot) + + var top = new MX.Object3D('.face.door.top') + top.width = doorWidth + top.height = height-doorHeight + top.x = doorOffset + top.y = (height+ doorHeight)/2 + top.el.style.backgroundColor = color + top.el.style.borderTop = borderThickness + 'px solid ' + borderColor + top.el.style.borderBottom = borderThickness + 'px solid ' + borderColor + this.add(top) + + } +})
\ No newline at end of file diff --git a/assets/javascripts/mx/primitives/mx.face.js b/assets/javascripts/mx/primitives/mx.face.js new file mode 100644 index 0000000..ac47ab4 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.face.js @@ -0,0 +1,41 @@ +MX.Face = MX.Object3D.extend({ + + // this will be called within the contructor + init: function (size, color, borderColor) { + + size = size || 100 + color = color || 'rgba(0, 255, 122, .1)' + borderColor = borderColor || '#0f3' + + // an Object3D's associated DOM node is the "el" property + this.el.classList.add('face') + + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2) + + var top = this.top = new MX.Object3D('.face') + top.rotationX = angle + top.y = size / 2 + + // adding children, must also be instances of Object3D + this.add(top) + + this.children.forEach(function (face) { + face.width = size - 2 + face.height = size - 2 + face.el.style.backgroundColor = color + face.el.style.border = '1px solid ' + borderColor + }) + + // this applies the updated CSS style + // required for any change to take effect + // when a parent object's update() is called + // all its children will be updated as well + this.update() + + // if this object's children won't move by themselves + this.updateChildren = false + } + + // other properties will be mixed into the prototype of the new constructor + +}) diff --git a/assets/javascripts/mx/primitives/mx.iframe.js b/assets/javascripts/mx/primitives/mx.iframe.js new file mode 100644 index 0000000..76ce603 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.iframe.js @@ -0,0 +1,19 @@ +MX.Iframe = MX.Object3D.extend({ + init: function (ops) { + + var layer = this.layer = new MX.Object3D() + layer.width = ops.width + layer.height = ops.height + +// this.add(layer) + this.width = ops.width + this.height = ops.height + + this.el.innerHTML = "<iframe src='" + ops.texture[i] + "' width='100%' height='100%' style='pointer-events: none;'>" + + this.dirty = true + this.updateChildren = true + this.update() + } + +}) diff --git a/assets/javascripts/mx/primitives/mx.image.js b/assets/javascripts/mx/primitives/mx.image.js new file mode 100644 index 0000000..92a8882 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.image.js @@ -0,0 +1,40 @@ +MX.Image = MX.Object3D.extend({ + init: function (ops) { + + this.type = "Image" + + var layer = this + layer.width = 0 + layer.height = 0 + + if (ops.src) this.loadTexture(ops) + + if (ops.className) { + layer.el.classList.add(ops.className) + } + layer.el.style.backgroundRepeat = 'no-repeat' + + this.dirty = true + this.updateChildren = true + this.update() + }, + + loadTexture: function(ops){ + var layer = this + var image = new Image() + image.onload = function(){ + layer.width = image.naturalWidth + layer.height = image.naturalHeight + layer.x = ops.x || 0 + layer.y = ops.y || 0 + layer.z = ops.z || 0 + layer.scale = ops.scale || 1 + layer.el.style.backgroundImage = "url(" + image.src + ")" + layer.el.classList.add('image') + layer.dirty = true + layer.update() + minimap.update() + } + image.src = ops.src; + } +}) diff --git a/assets/javascripts/mx/primitives/mx.texturedBox.js b/assets/javascripts/mx/primitives/mx.texturedBox.js new file mode 100644 index 0000000..34668d4 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.texturedBox.js @@ -0,0 +1,119 @@ +// Creates a box using a given texture image. +// Uses a texture image like this: +// +// ---------- ---------- +// | | | +// | top | bottom | +// | | | +// ---------- ---------- ---------- ---------- +// | | | | | +// | left | front | right | back | +// | | | | | +// ---------- ---------- ---------- ---------- +// +// See `examples/images/skins/` for some minecraft skin examples. + +// Options: +// +// - {number} `width` +// - {number} `height` +// - {number} `depth` +// - {string} `texture` path to texture image +// - {string} `classname` class to be added to dom element + +MX.TexturedBox = MX.Object3D.extend({ + + init: function (ops) { + + if (!ops.width || !ops.height || !ops.depth || (!ops.texture && !ops.classname)) { + console.warn('TextureBox: missing arguments') + return + } + + // faces + var angle = MX.rotationUnit === 'deg' ? 90 : (Math.PI / 2), + offsetX = ops.offset ? (ops.offset.x || 0) : 0, + offsetY = ops.offset ? (ops.offset.y || 0) : 0, + overlap = ops.overlap ? ops.overlap : 0 + var multiTexture = typeof ops.texture == "object"; + + var top = this.top = new MX.Object3D() + top.width = ops.width + top.height = ops.depth + top.rotationX = angle + top.y = ops.height / 2 - overlap + if (!multiTexture) + top.el.style.backgroundPosition = + (-(offsetX + ops.depth) + 'px ') + + (-offsetY + 'px') + + var bottom = this.bottom = new MX.Object3D() + bottom.width = ops.width + bottom.height = ops.depth + bottom.rotationX = -angle + bottom.y = -ops.height / 2 + overlap + if (!multiTexture) + bottom.el.style.backgroundPosition = + (-(offsetX + ops.depth + ops.width) + 'px ') + + (-offsetY + 'px') + + var left = this.left = new MX.Object3D() + left.width = ops.depth + left.height = ops.height + left.rotationY = -angle + left.x = -ops.width / 2 + overlap + if (!multiTexture) + left.el.style.backgroundPosition = + (-offsetX + 'px ') + + (-(offsetY + ops.depth) + 'px') + + var right = this.right = new MX.Object3D() + right.width = ops.depth + right.height = ops.height + right.rotationY = angle + right.x = ops.width / 2 - overlap + if (!multiTexture) + right.el.style.backgroundPosition = + (-(offsetX + ops.depth + ops.width) + 'px ') + + (-(offsetY + ops.depth) + 'px') + + var front = this.front = new MX.Object3D() + front.width = ops.width + front.height = ops.height + front.z = -ops.depth / 2 + overlap + if (!multiTexture) + front.el.style.backgroundPosition = + (-(offsetX + ops.depth) + 'px ') + + (-(offsetY + ops.depth) + 'px') + + var back = this.back = new MX.Object3D() + back.width = ops.width + back.height = ops.height + back.rotationY = angle * 2 + back.z = ops.depth / 2 - overlap + if (!multiTexture) + back.el.style.backgroundPosition = + (-(offsetX + ops.depth * 2 + ops.width) + 'px ') + + (-(offsetY + ops.depth) + 'px') + + this.add(top, bottom, left, right, front, back) + + this.children.forEach(function (c,i) { + if (multiTexture) { + c.el.style.backgroundImage = 'url(' + ops.texture[i] + ')' + } + else if (ops.texture) { + c.el.style.backgroundImage = 'url(' + ops.texture + ')' + } + if (ops.classname) { + c.el.classList.add(ops.classname) + } + c.el.style.backgroundRepeat = 'no-repeat' + }) + + this.update() + this.updateChildren = false + + } + +}) diff --git a/assets/javascripts/mx/primitives/mx.video.js b/assets/javascripts/mx/primitives/mx.video.js new file mode 100644 index 0000000..b48eff3 --- /dev/null +++ b/assets/javascripts/mx/primitives/mx.video.js @@ -0,0 +1,48 @@ +MX.Video = MX.Object3D.extend({ + init: function (ops) { + + this.type = "Video" + + if (ops.src) this.loadTexture(ops) + + this.children.forEach(function (c, i) { + if (ops.texture) { + } + else if (ops.classname) { + c.el.classList.add(ops.classname) + } + else { + } + c.el.style.backgroundRepeat = 'no-repeat' + }) + + this.dirty = true + this.updateChildren = true + this.update() + }, + + loadTexture: function(ops){ + var layer = this + var video = document.createElement('video') + video.setAttribute("autoplay", "") + video.setAttribute("loop", "") + video.setAttribute("muted", "muted") + video.addEventListener("loadedmetadata", function(){ + layer.width = video.videoWidth + layer.height = video.videoHeight + layer.x = ops.x || 0 + layer.y = ops.y || 0 + layer.z = ops.z || 0 + layer.scale = ops.scale || 1 + layer.el.appendChild(video) + layer.el.classList.add('video') + layer.dirty = true + layer.update() + minimap.update() + }) + video.src = ops.src + video.load() + } + +}) + |
