<header>
<h1><span>obelisk</span> buildr <em>stripped</em></h1>
<aside class="links">
<li><a target="_blank" href="https://bit.ly/1g08VKQ">full experiment</a></li>
<li><a target="_blank" href="https://bit.ly/1hnlAcE">github</a></li>
</aside>
</header>
<main>
<div id="buildr">
<canvas id="scene"></canvas>
</div>
<div id="palette" class="is-hidden">
<button class="red" data-tool="brush" data-value="#c82829">1</button>
<button class="orange" data-tool="brush" data-value="#f5871f">2</button>
<button class="yellow" data-tool="brush" data-value="#eab700">3</button>
<button class="green" data-tool="brush" data-value="#718c00">4</button>
<button class="aqua" data-tool="brush" data-value="#3e999f">5</button>
<button class="blue" data-tool="brush" data-value="#4271ae">6</button>
<button class="purple" data-tool="brush" data-value="#8959a8">7</button>
<button class="black" data-tool="brush" data-value="#4d4d4c">8</button>
<button class="white is-active" data-tool="brush" data-value="#eeeeee">9</button>
</div >
<div id="overlay" class="is-hidden"></div>
<div id="help" class="pane">
<ul>
<li><p><b>B</b>: Brush tool.</p></li>
<li><p><b>E</b>: Erase tool.</p></li>
<li><p><b>spacebar</b>: Color palette.</p></li>
<li><p><b>←</b>, <b>→</b>: Rotate view.</p></li>
<li><p><b>+</b>, <b>-</b>, <b>scroll</b>: Move draw layer up / down.</p></li>
<li><p><b>CTRL + Z</b>: Undo.</p></li>
<li><p><b>CTRL + S</b>: Save to gist.</p></li>
<li><p><b>N</b>: New art.</p></li>
<li><p><b>F</b>: Full canvas.</p></li>
<li><p><b>H</b>: Toggle this thing.</p></li>
</ul>
</div>
</main>
<script src="http://ngryman.sh/obelisk-buildr/libs.js" type="text/javascript"></script>
</body>
</html>
@font-face {
font-family: Lato;
src: url('https://github.com/ngryman/obelisk-buildr/raw/master/fonts/lato.woff?92048847') format('woff'),
url('https://github.com/ngryman/obelisk-buildr/raw/master/fonts/lato.ttf?92048847') format('truetype');
font-weight: normal;
font-style: normal;
}
* {
box-sizing: border-box;
box-sizing: border-box;
}
a,
a:active
a:visited {
color: #eee;
}
a:active {
text-decoration: none;
}
button {
margin: 2px;
padding: 0;
border: none;
outline: none;
}
html, body, main {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
body {
min-width: 320px;
background-color: #111;
color: #eee;
font-family: Lato, Arial, sans-serif;
user-select: none;
user-select: none;
user-select: none;
user-select: none;
}
main {
text-align: center;
}
@media (min-width: 700px) and (min-height: 700px), (min-height: 700px) {
main {
padding-top: 70px;
padding-bottom: 70px;
}
}
#buildr {
height: 100%;
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
opacity: .3;
transition: opacity 200ms;
backface-visibility: hidden;
backface-visibility: hidden;
}
#overlay.is-hidden {
opacity: 0;
pointer-events: none;
}
header {
display: block;
position: absolute;
z-index: 1;
padding: 6px;
width: 100%;
text-align: justify;
}
header::after {
content: '';
display: inline-block;
width: 100%;
height: 0;
font-size: 0;
line-height: 0;
}
header > * {
display: inline-block;
vertical-align: middle;
}
header h1 {
margin: 0;
height: 54px;
line-height: 54px;
color: #fff;
text-align: left;
font-weight: 300;
}
header h1 span {
color: #00B2B2;
}
header h1:before {
content: '';
display: inline-block;
vertical-align: text-top;
margin-right: 15px;
width: 40px;
height: 40px;
background-image: url(http://ngryman.sh/obelisk-buildr/logo.png);
background-position: left center;
background-repeat: no-repeat;
background-size: 40px;
animation: doodle 4s linear infinite;
animation: doodle 4s linear infinite;
}
header em {
color: #777;
font-size: 50%;
}
header aside {
text-align: right;
}
header li {
list-style-type: none;
}
@-webkit-keyframes doodle {
25% { transform: rotate(5deg); transform: rotate(5deg); }
75% { transform: rotate(-5deg); transform: rotate(-5deg); }
}
@keyframes doodle {
25% { transform: rotate(5deg); transform: rotate(5deg); }
75% { transform: rotate(-5deg); transform: rotate(-5deg); }
}
/**
* hide on small screens when they are in landscape mode
*
* [1]: using top instead of transform, because we can't have
* position fixed elements in transformed elements. This
* is needed for github auth buttons on small screens.
*/
@media (orientation: landscape) and (max-height: 400px) {
header {
top: -70px; /* [1] */
}
}
#help {
top: 70px;
left: 0;
width: 300px;
}
#help.is-hidden {
opacity: 0;
transform: translateX(-100%) scale(.8);
transform: translateX(-100%) scale(.8);
}
#palette {
position: fixed;
z-index: 1;
padding: 5px;
width: 200px;
background: rgba(17, 17, 17, .9);
opacity: 1;
text-align: left;
transform: scale(1);
transform: scale(1);
/*transform: translateX(-50%) translateY(-50%);*/
backface-visibility: hidden;
/*transform: translateX(-50%) translateY(-50%);*/
backface-visibility: hidden;
border: 1px solid #444;
box-shadow: 0 0 0 1px #000, 0 0 15px rgba(0, 0, 0, .5);
transition: opacity 100ms, transform 100ms;
transition: opacity 100ms, transform 100ms;
}
#palette.is-hidden {
opacity: 0;
transform: scale(.7);
transform: scale(.7);
pointer-events: none;
}
#palette button {
width: 30px;
height: 30px;
font-size: 10px;
}
#palette button.is-active {
box-shadow: 0 0 0 1px rgba(255, 255, 255, .4) inset;
}
#palette .red {
background: linear-gradient(#c82829, rgba(200, 40, 41, 0.80));
}
#palette .red:active {
background: linear-gradient(#af2829, rgba(175, 40, 41, 0.80));
}
#palette .orange {
background: linear-gradient(#f5871f, rgba(245, 135, 31, 0.80));
}
#palette .orange:active {
background: linear-gradient(#c8651f, rgba(200, 104, 31, 0.80));
}
#palette .yellow {
background: linear-gradient(#eab700, rgba(234, 183, 0, 0.80));
}
#palette .yellow:active {
background: linear-gradient(#bd8b00, rgba(189, 147, 0, 0.80));
}
#palette .green {
background: linear-gradient(#718c00, rgba(113, 140, 0, 0.80));
}
#palette .green:active {
background: linear-gradient(#475f00, rgba(71, 95, 0, 0.80));
}
#palette .aqua {
background: linear-gradient(#3e999f, rgba(62, 153, 159, 0.81));
}
#palette .aqua:active {
background: linear-gradient(#396c72, rgba(53, 108, 114, 0.81));
}
#palette .blue {
background: linear-gradient(#4271ae, rgba(66, 113, 174, 0.81));
}
#palette .blue:active {
background: linear-gradient(#404681, rgba(62, 71, 129, 0.81));
}
#palette .purple {
background: linear-gradient(#8959a8, rgba(137, 89, 168, 0.80));
}
#palette .purple:active {
background: linear-gradient(#5c3b7b, rgba(93, 59, 123, 0.80));
}
#palette .black {
background: linear-gradient(#4d4d4c, rgba(77, 77, 76, 0.80));
}
#palette .black:active {
background: linear-gradient(#20201f, rgba(32, 32, 31, 0.80));
}
#palette .white {
background: linear-gradient(#ddd, rgba(221, 221, 221, 0.80));
}
#palette .white:active {
background: linear-gradient(#b0b0b0, rgba(176, 176, 176, 0.80));
}
.pane {
position: fixed;
z-index: 1;
padding: 15px 30px;
background: rgba(17, 17, 17, .9);
border: 1px solid #444;
box-shadow: 0 0 0 1px #000, 0 0 15px rgba(0, 0, 0, .5);
transition: all 300ms;
border-radius: 2px;
}
.pane h2 {
font-weight: 300;
}
.pane ul {
margin-top: 20px;
padding-left: 20px;
}
.pane p {
text-align: left;
}
.pane b {
margin: 0 2px;
padding: 0 2px;
font-weight: 300;
color: #000;
background: #fff;
}
#scene {
width: 100%;
height: 100%;
backface-visibility: hidden;
backface-visibility: hidden;
}
@media (min-width: 800px) {
#scene {
width: 800px;
}
}
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
// TODO: move this elsewhere
obelisk.Point3D.prototype.clone = function() {
return new obelisk.Point3D(this.x, this.y, this.z);
};
/**
* Module dependencies.
*/
var ui = require('./ui'),
tool = require('./tool'),
pointer = require('./pointer'),
history = require('./history'),
storage = require('./storage');
/**
* Private variables
*/
var canvasEl = document.getElementById('scene');
var persistLock = false;
var autoSaveTimeout;
var resizeTimeout;
var fullCanvased = false;
/**
* Module declaration.
*/
var app = {};
/**
*
*/
app.init = function() {
ui.scene.init();
ui.palette.init();
tool.init(ui.scene)
.use('brush');
pointer.init(ui.scene)
.click(tool.click)
.drag(tool.drag);
bindShortcuts();
bindScroll();
bindResize();
touchDisclaimer();
// fetches initial art
//TODO
logCurious();
};
/**
* @private
*/
function logCurious() {
console.log('Hey! Curious or having bugs?');
console.log('Please post ideas or issues here: https://github.com/ngryman/obelisk-buildr/issues.');
console.log('You can play with the window.scene object.');
if (console.table) {
var methods = {
snapshot: { description: 'Returns a base64 image of the current scene.' },
load: { description: 'Loads a scene. Same format as the art.json file in gists.' },
save: { description: 'Returns data associated with the current scene. Same format as the art.json file in gists.' }
};
console.table(methods);
}
}
/**
* @private
*/
function bindShortcuts() {
document.addEventListener('keydown', onKeyDown);
}
/**
* @private
*/
function bindScroll() {
var isFirefox = /Firefox/i.test(navigator.userAgent);
document.addEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', onScroll);
}
/**
* @private
*/
function bindResize() {
window.addEventListener('resize', onResize);
}
/**
* @private
*/
function touchDisclaimer() {
var isChrome = /chrome/i.exec(navigator.userAgent),
isAndroid = /android/i.exec(navigator.userAgent),
hasTouch = 'ontouchstart' in window && !(isChrome && !isAndroid);
if (hasTouch)
ui.notification.error('Hey! For now, there is no real support for touch devices. Yeah i know...');
}
/**
* @param {boolean} silent
* @returns {object}
* @private
*/
function save(silent) {
}
/**
* @private
*/
function persist() {
}
/**
* @private
*/
function create() {
ui.scene.clear();
ui.notification.info('new craft!');
}
/**
* @private
*/
function fullCanvas() {
var display = fullCanvased ? 'block' : 'none';
document.querySelector('header').style.display = display;
document.querySelector('footer').style.display = display;
var padding = fullCanvased ? '70px' : '0px';
document.querySelector('main').style.paddingTop = padding;
document.querySelector('main').style.paddingBottom = padding;
ui.scene.resize();
fullCanvased = !fullCanvased;
}
/**
* @param {event} e
* @private
*/
function onKeyDown(e) {
switch (e.keyCode) {
// +
case 107:
ui.scene.adjustFloor(+1);
break;
// -
case 109:
ui.scene.adjustFloor(-1);
break;
// left
case 37:
ui.scene.rotate(+1);
break;
// right
case 39:
ui.scene.rotate(-1);
break;
// b
case 66:
tool.use('brush');
break;
// e
case 69:
tool.use('erase');
break;
// space bar
case 32:
ui.palette.toggle();
break;
// ctrl + z
case 90:
if (e.ctrlKey) history.back();
break;
// n
case 78:
create();
break;
// f
case 70:
fullCanvas();
break;
// h
case 72:
e.preventDefault();
ui.help.toggle();
break;
}
// 123456789
if (e.keyCode >= 49 && e.keyCode <= 57)
tool.use('brush').set(ui.palette.color(e.keyCode - 49));
}
/**
* @param {event} e
* @private
*/
function onScroll(e) {
var delta = e.detail ? -e.detail : e.wheelDelta;
ui.scene.adjustFloor(delta > 0 ? -1 : +1);
}
/**
* @param {event} e
* @private
*/
function onResize(e) {
// hide on first event
if (null == resizeTimeout)
canvasEl.style.visibility = 'hidden';
// debounce scene resize
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
ui.scene.resize();
canvasEl.style.visibility = 'visible';
resizeTimeout = null;
}, 100);
}
/**
* @private
*/
function onAutoSave() {
}
/**
* Global export.
*/
window.app = app;
/**
* Exports for hackers
*/
window.scene = ui.scene;
},{"./history":3,"./pointer":4,"./storage":5,"./tool":6,"./ui":11}],2:[function(require,module,exports){
},{}],3:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Private variables
*/
var stack = [];
var sequence = null;
/**
* Module declaration.
*/
var history = {};
/**
*
* @param {function} fn
*/
history.push = function(fn) {
if (!sequence)
return stack.push(fn);
sequence.push(fn);
};
/**
*
*/
history.startSequence = function() {
sequence = [];
};
/**
*
*/
history.stopSequence = function() {
var seq = sequence;
stack.push(function() {
seq.forEach(function(action) {
action();
});
});
sequence = null;
};
/**
*
* @returns {boolean}
*/
history.isSequenced = function() {
return (null != sequence);
};
/**
*
*/
history.back = function() {
if (0 === stack.length) return;
stack.pop()();
};
/**
* Exports.
*/
module.exports = history;
},{}],4:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var history = require('./history');
/**
* Private variables
*/
var canvasEl = document.getElementById('scene');
var canvasOffset = new obelisk.Point();
var scene;
var x, y;// TODO: screenPos
var viewPoint = new obelisk.Point3D(); // TODO: viewPos
var loopStarted = false;
var loopStopIn = 0;
var down = false;
var dragging = false;
var clickHandler;
var dragHandler;
var moveHandler;
/**
* Module declaration.
*/
var pointer = {};
/**
*
* @param {scene} _scene
* @returns {pointer}
*/
pointer.init = function(_scene) {
var main = document.querySelector('#buildr');
main.addEventListener('mousedown', onDown);
main.addEventListener('mousemove', onMove);
main.addEventListener('mouseup', onUp);
main.addEventListener('mouseleave', onUp);
window.addEventListener('resize', onResize);
Loop.on('tick', apply);
onResize();
scene = _scene;
return pointer;
};
/**
*
* @param {function} handler
* @returns {pointer}
*/
pointer.click = function(handler) {
clickHandler = handler;
return pointer;
};
/**
*
* @param {function} handler
* @returns {pointer}
*/
pointer.drag = function(handler) {
dragHandler = handler;
return pointer;
};
/**
*
* @param {function} handler
* @returns {pointer}
*/
pointer.move = function(handler) {
moveHandler = handler;
return pointer;
};
/**
* @private
*/
function onDown() {
down = true;
}
/**
* @private
*/
function onUp() {
// may happen when called by mouseleave
if (!down) return;
down = false;
if (!dragging) {
if (clickHandler) clickHandler();
return;
}
dragging = false;
if (history.isSequenced())
history.stopSequence();
}
/**
* @param {event} e
* @private
*/
function onMove(e) {
x = e.pageX - canvasOffset.x;
y = e.pageY - canvasOffset.y;
if (down && !dragging)
dragging = true;
if (!loopStarted) {
Loop.start();
loopStarted = true;
}
loopStopIn = 1;
}
/**
* @private
*/
function onResize() {
canvasOffset.x = canvasEl.offsetLeft;
canvasOffset.y = canvasEl.offsetTop;
}
/**
* @private
*/
function apply() {
scene.screenToView(x, y, viewPoint);
scene.select(viewPoint);
if (dragging) {
if (dragHandler) dragHandler();
return;
}
if (moveHandler) moveHandler();
if (0 === loopStopIn) {
Loop.stop();
loopStarted = false;
}
loopStopIn--;
}
/**
* Exports.
*/
module.exports = pointer;
},{"./history":3}],5:[function(require,module,exports){
},{"./github":2}],6:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var tools = {
brush: require('./tools/brush'),
erase: require('./tools/erase')
};
/**
* Private variables
*/
var scene;
var current;
/**
* Module declaration.
*/
var tool = {};
/**
*
* @param _scene
* @returns {tool}
*/
tool.init = function(_scene) {
scene = _scene;
return tool;
};
/**
*
* @param type
* @returns {tool}
*/
tool.use = function(type) {
current = tools[type];
return tool;
};
/**
*
* @param value
* @returns {tool}
*/
tool.set = function(value) {
if (current.set) current.set(value);
return tool;
};
/**
*
* @returns {tool}
*/
tool.click = function() {
current.click(scene);
return tool;
};
/**
*
* @returns {tool}
*/
tool.drag = function() {
current.drag(scene);
return tool;
};
/**
* Exports.
*/
module.exports = tool;
},{"./tools/brush":7,"./tools/erase":8}],7:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var history = require('../history');
/**
* Private variables
*/
var color = obelisk.ColorPattern.GRAY;
/**
* Module declaration.
*/
var brush = {};
/**
*
* @param {string} value
*/
brush.set = function(value) {
// strip #
value = value.slice(1);
// convert to number
value = parseInt(value, 16);
color = value;
};
/**
*
* @param {scene} scene
*/
brush.click = function(scene) {
if (scene.add(color))
history.push(scene.remove.bind(scene, scene.selected().clone()));
};
/**
*
* @param {scene} scene
*/
brush.drag = function(scene) {
if (scene.add(color)) {
if (!history.isSequenced())
history.startSequence();
history.push(scene.remove.bind(scene, scene.selected().clone()));
}
};
/**
* Exports.
*/
module.exports = brush;
},{"../history":3}],8:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var history = require('../history');
/**
* Module declaration.
*/
var erase = {};
/**
*
* @param {scene} scene
*/
erase.click = function(scene) {
var color = scene.color();
if (scene.remove())
history.push(scene.add.bind(scene,
color,
scene.selected().clone()
));
};
/**
*
* @param {scene} scene
*/
erase.drag = function(scene) {
var color = scene.color();
if (scene.remove()) {
if (!history.isSequenced())
history.startSequence();
history.push(scene.add.bind(scene,
color,
scene.selected().clone()
));
}
};
/**
* Exports.
*/
module.exports = erase;
},{"../history":3}],9:[function(require,module,exports){
},{"./../github":2}],10:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Private variables
*/
var el = document.querySelector('#help');
/**
* Module declaration.
*/
var help = {};
help.toggle = function() {
el.classList.toggle('is-hidden');
};
/**
* Exports.
*/
module.exports = help;
},{}],11:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Exports.
*/
module.exports = {
auth: require('./auth'),
notification: require('./notification'),
palette: require('./palette'),
scene: require('./scene'),
welcome: require('./welcome'),
help: require('./help')
};
},{"./auth":9,"./help":10,"./notification":12,"./palette":13,"./scene":14,"./welcome":15}],12:[function(require,module,exports){
},{}],13:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
*/
var tool = require('./../tool');
/**
* Private variables
*/
var el = document.getElementById('palette');
var overlayEl = document.getElementById('overlay');
var active = document.querySelector('#palette .is-active');
var visible = false;
var x, y;
var width, height;
/**
* Module declaration.
*/
var palette = {};
/**
*
*/
palette.init = function() {
var style = getComputedStyle(el);
width = parseInt(style.width);
height = parseInt(style.height);
window.addEventListener('mousemove', function(e) {
x = e.clientX;
y = e.clientY;
});
el.addEventListener('click', function(e) {
var btn = e.target;
if ('BUTTON' != btn.tagName) return;
if (active)
active.classList.remove('is-active');
btn.classList.add('is-active');
// tool selection and value
tool.use(btn.dataset.tool).set(btn.dataset.value);
active = e.target;
});
};
/**
*
*/
palette.toggle = function() {
if (!visible) {
el.style.left = (x - width / 2) + 'px';
el.style.top = (y - height / 2) + 'px';
}
el.classList.toggle('is-hidden');
overlayEl.classList.toggle('is-hidden');
visible = !visible;
};
/**
*
* @param {number} index
* @returns {string}
*/
palette.color = function(index) {
var btn = el.querySelector('button:nth-child(' + (index + 1) + ')');
return btn.dataset.value;
};
/**
* Exports.
*/
module.exports = palette;
},{"./../tool":6}],14:[function(require,module,exports){
/*!
* obelisk-builder
* Copyright (c) 2013 Nicolas Gryman <ngryman@gmail.com>
* MIT Licensed
*/
'use strict';
/**
* Private variables
*/
var canvasEl = document.getElementById('scene');
var SIZE = 20;
var BLOCK_SIZE = 20;
var view;
var floor;
var cubes;
var blocks;
var origin;
var changed = false;
var changedHandler;
/**
* Module declaration.
*/
var scene = {};
/**
*
* @returns {scene}
*/
scene.init = function() {
cubes = {};
cubes[obelisk.ColorPattern.GRAY] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, obelisk.ColorPattern.GRAY);
floor = {
normal: createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF222222'),
elevated: createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF222222'),
highlighted: createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF444444'),
elevatedHighlighted: createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF555555'),
selected: new obelisk.Point3D(-1, -1, 0),
offset: 0
};
blocks = matrix(SIZE);
scene.resize();
return scene;
};
/**
*
*/
scene.resize = function() {
// 1:1 ratio
var style = getComputedStyle(canvasEl);
canvasEl.width = parseInt(style.width);
canvasEl.height = parseInt(style.height);
origin = new obelisk.Point(canvasEl.width / 2, canvasEl.height / 2);
view = new obelisk.PixelView(canvasEl, new obelisk.Point(
origin.x, origin.y
));
var oldBlockSize = BLOCK_SIZE;
// small screens
if (canvasEl.width < 800 || canvasEl.height < 800) {
var size = Math.min(canvasEl.width, canvasEl.height);
// compute new block size
BLOCK_SIZE = Math.floor(size / 40);
// ensure it's even (obelisk needs it)
BLOCK_SIZE = BLOCK_SIZE - (BLOCK_SIZE % 2);
}
// ensure block size comes back to its original size
else
BLOCK_SIZE = 20;
// adapt existing geom to the correct size
if (oldBlockSize != BLOCK_SIZE) {
for (var color in cubes) {
if (!cubes.hasOwnProperty(color)) continue;
cubes[color] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, parseInt(color));
}
floor.normal = createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF222222');
floor.elevated = createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF222222');
floor.highlighted = createBrick(BLOCK_SIZE, BLOCK_SIZE, '0xFF444444');
floor.elevatedHighlighted = createBrick(BLOCK_SIZE + 2, BLOCK_SIZE + 2, '0xFF555555');
}
scene.draw();
};
/**
*
*/
scene.draw = function() {
view.clear();
drawBlocks(view, blocks, cubes, 0, floor.offset);
drawFloor(view, floor);
drawBlocks(view, blocks, cubes, floor.offset, SIZE);
};
/**
*
* @param {number} x
* @param {number} y
* @param {obelisk.Point3D} point
*/
scene.screenToView = function(x, y, point) {
x -= origin.x;
y -= origin.y;
point.x = Math.floor(((x + 2 * y) / 2) / BLOCK_SIZE) + floor.offset;
point.y = Math.floor(((-x + 2 * y) / 2) / BLOCK_SIZE) + floor.offset;
point.z = floor.offset;
};
/**
*
* @param {obelisk.Point3D} point
*/
scene.select = function(point) {
clampBounds(point);
floor.selected.x = point.x;
floor.selected.y = point.y;
floor.selected.z = point.z;
scene.draw();
};
/**
*
* @returns {obelisk.Point}
*/
scene.selected = function() {
return floor.selected;
};
/**
*
* @param {obelisk.Point3D} point
* @returns {number|null}
*/
scene.color = function(point) {
point = point || scene.selected();
var block = blocks[point.x][point.y][point.z];
if (!block) return null;
return block.color;
};
/**
*
* @param {number} color
* @param {obelisk.Point3D} point
* @returns {boolean}
*/
scene.add = function(color, point) {
point = point || floor.selected;
// already exists
if (null != blocks[point.x][point.y][point.z]) return false;
// if a cube with the given color does not exist, create it
if (null == cubes[color])
cubes[color] = createCube(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, color);
var block = point.clone();
block.color = color;
blocks[block.x][block.y][block.z] = block;
scene.draw();
changed = true;
if (changedHandler) changedHandler();
return true;
};
/**
*
* @param {obelisk.Point3D} point
* @returns {boolean}
*/
scene.remove = function(point) {
point = point || floor.selected;
if (null == blocks[point.x][point.y][point.z]) return false;
blocks[point.x][point.y][point.z] = null;
scene.draw();
changed = true;
if (changedHandler) changedHandler();
return true;
};
/**
*
* @param {number} delta
*/
scene.adjustFloor = function(delta) {
floor.offset += delta;
if (floor.offset < 0) floor.offset = 0;
else if (floor.offset >= SIZE) floor.offset = SIZE - 1;
scene.draw();
};
/**
*
* @param {number} direction
*/
scene.rotate = function(direction) {
var b = blocks,
n = SIZE,
x, y, z, start, end;
// transpose
for (z = 0; z < n; z++)
for (x = 0; x < n; x++)
for (y = 0; y < n; y++)
if (x < y) swap(b, x, y, y, x, z);
if (1 === direction) {
// reverse rows
for (z = 0; z < n; z++)
for (x = 0; x < n; x++)
for (start = 0, end = n - 1; start < end; start++, end--)
swap(b, x, start, x, end, z);
}
else if (-1 == direction) {
// reverse cols
for (z = 0; z < n; z++)
for (y = 0; y < n; y++)
for (start = 0, end = n - 1; start < end; start++, end--)
swap(b, start, y, end, y, z);
}
scene.draw();
};
/**
*
* @param {boolean} silent
* @returns {object}
*/
scene.save = function(silent) {
var data = [];
// creates a color table
var colors = [], colorsHash = {};
for (var p in cubes) {
if (!cubes.hasOwnProperty(p)) continue;
colorsHash[p] = colors.length;
colors.push(p);
}
// basically "compact" the memory structure by only saving existing blocks,
// referencing colors in color table, with short properties name
for (var x = 0; x < SIZE; x++) {
for (var y = 0; y < SIZE; y++) {
for (var z = 0; z < SIZE; z++) {
if (null != blocks[x][y][z]) {
var b = blocks[x][y][z];
data.push({
x: b.x,
y: b.y,
z: b.z,
c: colorsHash[b.color]
});
}
}
}
}
if (!silent) changed = false;
return {
colors: colors,
data: data
};
};
/**
*
* @param {object} data
*/
scene.load = function(data) {
var i;
// ensure colors are numbers
var colors = data.colors;
for (i = 0; i < colors.length; i++)
colors[i] = parseInt(colors[i]);
data = data.data;
blocks = matrix(SIZE);
for (i = 0; i < data.length; i++)
scene.add(colors[data[i].c], new obelisk.Point3D(data[i].x, data[i].y, data[i].z));
scene.draw();
changed = false;
};
/**
*
* @returns {string}
*/
scene.snapshot = function() {
// isolate art
view.clear();
view.context.fillStyle = '#222222';
view.context.fillRect(0, 0, canvasEl.width, canvasEl.height);
drawBlocks(view, blocks, cubes, 0, SIZE);
var image = canvasEl.toDataURL("image/png");
scene.draw();
return image;
};
/**
*
*/
scene.clear = function() {
blocks = matrix(SIZE);
scene.draw();
};
/**
*
* @param {function} callback
* @returns {boolean|undefined}
*/
scene.changed = function(callback) {
if (!callback) return changed;
changedHandler = callback;
return this;
};
/**
*
* @param {number} width
* @param {number} height
* @param {string} hexColor
* @returns {obelisk.Brick}
* @private
*/
function createBrick(width, height, hexColor) {
var dimension = new obelisk.BrickDimension(width, height);
var color = new obelisk.SideColor().getByInnerColor(hexColor);
return new obelisk.Brick(dimension, color, false);
}
/**
*
* @param {number} width
* @param {number} height
* @param {number} depth
* @param {string} hexColor
* @returns {obelisk.Cube}
* @private
*/
function createCube(width, height, depth, hexColor) {
var dimension = new obelisk.CubeDimension(width, height, depth);
var color = new obelisk.CubeColor(
obelisk.ColorGeom.applyBrightness(hexColor, -20 * 4),
obelisk.ColorGeom.applyBrightness(hexColor, 60),
obelisk.ColorGeom.applyBrightness(hexColor, -20 * 2),
obelisk.ColorGeom.applyBrightness(hexColor, -20),
hexColor
);
return new obelisk.Cube(dimension, color);
}
/**
*
* @param {obelisk.Point3D} point
* @private
*/
function clampBounds(point) {
if (point.x < 0) point.x = 0;
else if (point.x >= SIZE) point.x = SIZE - 1;
if (point.y < 0) point.y = 0;
else if (point.y >= SIZE) point.y = SIZE - 1;
if (point.z < 0) point.z = 0;
else if (point.z >= SIZE) point.z = SIZE - 1;
}
/**
*
* @param {number} size
* @returns {array}
* @private
*/
function matrix(size) {
var m = new Array(size);
for (var x = 0; x < size; x++) {
m[x] = new Array(size);
for (var y = 0; y < size; y++) {
m[x][y] = new Array(size);
}
}
return m;
}
/**
*
* @param {number} m
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} z
* @private
*/
function swap(m, x1, y1, x2, y2, z) {
var tmp = m[x2][y2][z], c;
c = m[x2][y2][z] = m[x1][y1][z];
if (c) {
c.x = x2;
c.y = y2;
}
c = m[x1][y1][z] = tmp;
if (c) {
c.x = x1;
c.y = y1;
}
}
/**
*
* @param {obelisk.PixelView} view
* @param {object} floor
* @private
*/
function drawFloor(view, floor) {
var normal = floor.normal,
highlighted = floor.highlighted,
point = new obelisk.Point3D();
if (floor.offset > 0) {
normal = floor.elevated;
highlighted = floor.elevatedHighlighted;
view.context.globalAlpha = 0.7;
}
for (var x = 0; x < 20; x++) {
for (var y = 0; y < 20; y++) {
if (x == floor.selected.x && y == floor.selected.y) continue;
point.x = x * BLOCK_SIZE;
point.y = y * BLOCK_SIZE;
point.z = floor.offset * BLOCK_SIZE;
view.renderObject(normal, point);
}
}
if (-1 != floor.selected.x) {
point.x = floor.selected.x * BLOCK_SIZE;
point.y = floor.selected.y * BLOCK_SIZE;
point.z = floor.offset * BLOCK_SIZE;
view.renderObject(highlighted, point);
}
view.context.globalAlpha = 1;
}
/**
*
* @param {obelisk.PixelView} view
* @param {array} blocks
* @param {object} cubes
* @param {number} startZ
* @param {number} endZ
* @private
*/
function drawBlocks(view, blocks, cubes, startZ, endZ) {
var point = new obelisk.Point3D();
for (var x = 0; x < SIZE; x++) {
for (var y = 0; y < SIZE; y++) {
for (var z = startZ; z < endZ; z++) {
var block = blocks[x][y][z];
if (!block) continue;
point.x = block.x * BLOCK_SIZE;
point.y = block.y * BLOCK_SIZE;
point.z = block.z * BLOCK_SIZE;
view.renderObject(cubes[block.color], point);
}
}
}
}
/**
* Exports.
*/
module.exports = scene;
},{}],15:[function(require,module,exports){
},{}]},{},[1])
app.init();
// loads heart
scene.load({"colors":["13117481","15658734"],"data":[{"x":0,"y":9,"z":10,"c":0},{"x":0,"y":9,"z":11,"c":0},{"x":0,"y":9,"z":12,"c":0},{"x":0,"y":9,"z":13,"c":0},{"x":1,"y":9,"z":8,"c":0},{"x":1,"y":9,"z":9,"c":0},{"x":1,"y":9,"z":10,"c":0},{"x":1,"y":9,"z":11,"c":0},{"x":1,"y":9,"z":12,"c":0},{"x":1,"y":9,"z":13,"c":0},{"x":1,"y":9,"z":14,"c":0},{"x":2,"y":9,"z":7,"c":0},{"x":2,"y":9,"z":8,"c":0},{"x":2,"y":9,"z":9,"c":0},{"x":2,"y":9,"z":10,"c":0},{"x":2,"y":9,"z":11,"c":0},{"x":2,"y":9,"z":12,"c":0},{"x":2,"y":9,"z":13,"c":0},{"x":2,"y":9,"z":14,"c":0},{"x":2,"y":9,"z":15,"c":0},{"x":3,"y":9,"z":6,"c":0},{"x":3,"y":9,"z":7,"c":0},{"x":3,"y":9,"z":8,"c":0},{"x":3,"y":9,"z":9,"c":0},{"x":3,"y":9,"z":10,"c":0},{"x":3,"y":9,"z":11,"c":0},{"x":3,"y":9,"z":12,"c":0},{"x":3,"y":9,"z":13,"c":0},{"x":3,"y":9,"z":14,"c":0},{"x":3,"y":9,"z":15,"c":0},{"x":3,"y":9,"z":16,"c":0},{"x":4,"y":9,"z":5,"c":0},{"x":4,"y":9,"z":6,"c":0},{"x":4,"y":9,"z":7,"c":0},{"x":4,"y":9,"z":8,"c":0},{"x":4,"y":9,"z":9,"c":0},{"x":4,"y":9,"z":10,"c":0},{"x":4,"y":9,"z":11,"c":0},{"x":4,"y":9,"z":12,"c":0},{"x":4,"y":9,"z":13,"c":0},{"x":4,"y":9,"z":14,"c":0},{"x":4,"y":9,"z":15,"c":0},{"x":4,"y":9,"z":16,"c":0},{"x":4,"y":9,"z":17,"c":0},{"x":5,"y":9,"z":4,"c":0},{"x":5,"y":9,"z":5,"c":0},{"x":5,"y":9,"z":6,"c":0},{"x":5,"y":9,"z":7,"c":0},{"x":5,"y":9,"z":8,"c":0},{"x":5,"y":9,"z":9,"c":0},{"x":5,"y":9,"z":10,"c":0},{"x":5,"y":9,"z":11,"c":0},{"x":5,"y":9,"z":12,"c":0},{"x":5,"y":9,"z":13,"c":0},{"x":5,"y":9,"z":14,"c":0},{"x":5,"y":9,"z":15,"c":0},{"x":5,"y":9,"z":16,"c":0},{"x":5,"y":9,"z":17,"c":0},{"x":6,"y":9,"z":3,"c":0},{"x":6,"y":9,"z":4,"c":0},{"x":6,"y":9,"z":5,"c":0},{"x":6,"y":9,"z":6,"c":0},{"x":6,"y":9,"z":7,"c":0},{"x":6,"y":9,"z":8,"c":0},{"x":6,"y":9,"z":9,"c":0},{"x":6,"y":9,"z":10,"c":0},{"x":6,"y":9,"z":11,"c":0},{"x":6,"y":9,"z":12,"c":0},{"x":6,"y":9,"z":13,"c":0},{"x":6,"y":9,"z":14,"c":0},{"x":6,"y":9,"z":15,"c":0},{"x":6,"y":9,"z":16,"c":0},{"x":6,"y":9,"z":17,"c":0},{"x":7,"y":9,"z":2,"c":0},{"x":7,"y":9,"z":3,"c":0},{"x":7,"y":9,"z":4,"c":0},{"x":7,"y":9,"z":5,"c":0},{"x":7,"y":9,"z":6,"c":0},{"x":7,"y":9,"z":7,"c":0},{"x":7,"y":9,"z":8,"c":0},{"x":7,"y":9,"z":9,"c":0},{"x":7,"y":9,"z":10,"c":0},{"x":7,"y":9,"z":11,"c":0},{"x":7,"y":9,"z":12,"c":0},{"x":7,"y":9,"z":13,"c":0},{"x":7,"y":9,"z":14,"c":0},{"x":7,"y":9,"z":15,"c":0},{"x":7,"y":9,"z":16,"c":0},{"x":8,"y":9,"z":1,"c":0},{"x":8,"y":9,"z":2,"c":0},{"x":8,"y":9,"z":3,"c":0},{"x":8,"y":9,"z":4,"c":0},{"x":8,"y":9,"z":5,"c":0},{"x":8,"y":9,"z":6,"c":0},{"x":8,"y":9,"z":7,"c":0},{"x":8,"y":9,"z":8,"c":0},{"x":8,"y":9,"z":9,"c":0},{"x":8,"y":9,"z":10,"c":0},{"x":8,"y":9,"z":11,"c":0},{"x":8,"y":9,"z":12,"c":0},{"x":8,"y":9,"z":13,"c":0},{"x":8,"y":9,"z":14,"c":0},{"x":8,"y":9,"z":15,"c":0},{"x":9,"y":9,"z":0,"c":0},{"x":9,"y":9,"z":1,"c":0},{"x":9,"y":9,"z":2,"c":0},{"x":9,"y":9,"z":3,"c":0},{"x":9,"y":9,"z":4,"c":0},{"x":9,"y":9,"z":5,"c":0},{"x":9,"y":9,"z":6,"c":0},{"x":9,"y":9,"z":7,"c":0},{"x":9,"y":9,"z":8,"c":0},{"x":9,"y":9,"z":9,"c":0},{"x":9,"y":9,"z":10,"c":0},{"x":9,"y":9,"z":11,"c":0},{"x":9,"y":9,"z":12,"c":0},{"x":9,"y":9,"z":13,"c":0},{"x":9,"y":9,"z":14,"c":0},{"x":10,"y":9,"z":0,"c":0},{"x":10,"y":9,"z":1,"c":0},{"x":10,"y":9,"z":2,"c":0},{"x":10,"y":9,"z":3,"c":0},{"x":10,"y":9,"z":4,"c":0},{"x":10,"y":9,"z":5,"c":0},{"x":10,"y":9,"z":6,"c":0},{"x":10,"y":9,"z":7,"c":0},{"x":10,"y":9,"z":8,"c":0},{"x":10,"y":9,"z":9,"c":0},{"x":10,"y":9,"z":10,"c":0},{"x":10,"y":9,"z":11,"c":0},{"x":10,"y":9,"z":12,"c":0},{"x":10,"y":9,"z":13,"c":0},{"x":10,"y":9,"z":14,"c":0},{"x":11,"y":9,"z":1,"c":0},{"x":11,"y":9,"z":2,"c":0},{"x":11,"y":9,"z":3,"c":0},{"x":11,"y":9,"z":4,"c":0},{"x":11,"y":9,"z":5,"c":0},{"x":11,"y":9,"z":6,"c":0},{"x":11,"y":9,"z":7,"c":0},{"x":11,"y":9,"z":8,"c":0},{"x":11,"y":9,"z":9,"c":0},{"x":11,"y":9,"z":10,"c":0},{"x":11,"y":9,"z":11,"c":0},{"x":11,"y":9,"z":12,"c":0},{"x":11,"y":9,"z":13,"c":0},{"x":11,"y":9,"z":14,"c":0},{"x":11,"y":9,"z":15,"c":0},{"x":12,"y":9,"z":2,"c":0},{"x":12,"y":9,"z":3,"c":0},{"x":12,"y":9,"z":4,"c":0},{"x":12,"y":9,"z":5,"c":0},{"x":12,"y":9,"z":6,"c":0},{"x":12,"y":9,"z":7,"c":0},{"x":12,"y":9,"z":8,"c":0},{"x":12,"y":9,"z":9,"c":0},{"x":12,"y":9,"z":10,"c":0},{"x":12,"y":9,"z":11,"c":0},{"x":12,"y":9,"z":12,"c":0},{"x":12,"y":9,"z":13,"c":0},{"x":12,"y":9,"z":14,"c":0},{"x":12,"y":9,"z":15,"c":0},{"x":12,"y":9,"z":16,"c":0},{"x":13,"y":9,"z":3,"c":0},{"x":13,"y":9,"z":4,"c":0},{"x":13,"y":9,"z":5,"c":0},{"x":13,"y":9,"z":6,"c":0},{"x":13,"y":9,"z":7,"c":0},{"x":13,"y":9,"z":8,"c":0},{"x":13,"y":9,"z":9,"c":0},{"x":13,"y":9,"z":10,"c":0},{"x":13,"y":9,"z":11,"c":0},{"x":13,"y":9,"z":12,"c":0},{"x":13,"y":9,"z":13,"c":0},{"x":13,"y":9,"z":14,"c":0},{"x":13,"y":9,"z":15,"c":0},{"x":13,"y":9,"z":16,"c":0},{"x":13,"y":9,"z":17,"c":0},{"x":14,"y":9,"z":4,"c":0},{"x":14,"y":9,"z":5,"c":0},{"x":14,"y":9,"z":6,"c":0},{"x":14,"y":9,"z":7,"c":0},{"x":14,"y":9,"z":8,"c":0},{"x":14,"y":9,"z":9,"c":0},{"x":14,"y":9,"z":10,"c":0},{"x":14,"y":9,"z":11,"c":0},{"x":14,"y":9,"z":12,"c":0},{"x":14,"y":9,"z":13,"c":0},{"x":14,"y":9,"z":14,"c":0},{"x":14,"y":9,"z":15,"c":0},{"x":14,"y":9,"z":16,"c":0},{"x":14,"y":9,"z":17,"c":0},{"x":15,"y":9,"z":5,"c":0},{"x":15,"y":9,"z":6,"c":0},{"x":15,"y":9,"z":7,"c":0},{"x":15,"y":9,"z":8,"c":0},{"x":15,"y":9,"z":9,"c":0},{"x":15,"y":9,"z":10,"c":0},{"x":15,"y":9,"z":11,"c":0},{"x":15,"y":9,"z":12,"c":0},{"x":15,"y":9,"z":13,"c":0},{"x":15,"y":9,"z":14,"c":0},{"x":15,"y":9,"z":15,"c":0},{"x":15,"y":9,"z":16,"c":0},{"x":15,"y":9,"z":17,"c":0},{"x":16,"y":9,"z":6,"c":0},{"x":16,"y":9,"z":7,"c":0},{"x":16,"y":9,"z":8,"c":0},{"x":16,"y":9,"z":9,"c":0},{"x":16,"y":9,"z":10,"c":0},{"x":16,"y":9,"z":11,"c":0},{"x":16,"y":9,"z":12,"c":0},{"x":16,"y":9,"z":13,"c":0},{"x":16,"y":9,"z":14,"c":0},{"x":16,"y":9,"z":15,"c":0},{"x":16,"y":9,"z":16,"c":0},{"x":17,"y":9,"z":7,"c":0},{"x":17,"y":9,"z":8,"c":0},{"x":17,"y":9,"z":9,"c":0},{"x":17,"y":9,"z":10,"c":0},{"x":17,"y":9,"z":11,"c":0},{"x":17,"y":9,"z":12,"c":0},{"x":17,"y":9,"z":13,"c":0},{"x":17,"y":9,"z":14,"c":0},{"x":17,"y":9,"z":15,"c":0},{"x":18,"y":9,"z":8,"c":0},{"x":18,"y":9,"z":9,"c":0},{"x":18,"y":9,"z":10,"c":0},{"x":18,"y":9,"z":11,"c":0},{"x":18,"y":9,"z":12,"c":0},{"x":18,"y":9,"z":13,"c":0},{"x":18,"y":9,"z":14,"c":0},{"x":19,"y":9,"z":10,"c":0},{"x":19,"y":9,"z":11,"c":0},{"x":19,"y":9,"z":12,"c":0},{"x":19,"y":9,"z":13,"c":0}]});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.