Last active
April 11, 2023 01:24
-
-
Save hperantunes/1100163 to your computer and use it in GitHub Desktop.
hexagons test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | |
<title>Hexagons Test</title> | |
<!--<link href='http://fonts.googleapis.com/css?family=Orbitron' rel='stylesheet' type='text/css'>--> | |
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/2.7.4/d3.geom.min.js"></script> | |
<style type="text/css"> | |
svg { | |
border: none; | |
background: white; | |
} | |
.bounds { | |
stroke: red; | |
fill: none; | |
} | |
.tile { | |
stroke: black; | |
stroke-width: 1px; | |
fill: black; | |
fill-opacity: 0.05; | |
} | |
.tile.resource { | |
fill: grey; | |
fill-opacity: 0.8; | |
} | |
.entity { | |
stroke: none; | |
fill: orange; | |
} | |
.entity.structure { | |
stroke: none; | |
fill: orange; | |
} | |
.entity.structure.own { | |
} | |
.entity.structure.other { | |
} | |
.entity.ship { | |
fill: gray; | |
} | |
.entity.ship.own { | |
fill: navy; | |
} | |
.entity.ship.enemy { | |
fill: darkred; | |
} | |
.over { | |
fill: red !important; | |
fill-opacity: 0.8 !important; | |
} | |
.selected { | |
fill: yellow; | |
fill-opacity: 0.8 !important; | |
} | |
.neighbor { | |
fill: lime !important; | |
fill-opacity: 0.4; | |
} | |
div.tooltip { | |
background: white; | |
padding: 0px 2px; | |
position: absolute; | |
z-index: 10; | |
visibility: hidden; | |
/*font-weight: 400;*/ | |
font-size: 0.8em; | |
font-family: /*'Orbitron',*/ sans-serif; | |
} | |
div.visible { | |
visibility: visible !important; | |
} | |
</style> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
var data = [], | |
entities = []; | |
var svg, | |
tooltip; | |
(function() { | |
window.onload = function() { | |
// binding events through the entire document | |
document.addEventListener('keydown', keyDown, false); | |
document.addEventListener('keyup', keyUp, false); | |
document.addEventListener('keyup', kCheck, false); | |
initialize(); | |
console.log('total tiles: ' + totalTiles()); | |
console.log('mapped keys:'); | |
for(var i in keyMap) { | |
if (keyMap.hasOwnProperty(i)) { | |
console.log('\t' + keyMap[i][0]); | |
} | |
} | |
}; | |
var size = 40, // hexagon size | |
radius = 100, // map radius | |
tilted = false; // true is horizontal alignment | |
var w = 960, // width | |
h = 500, // height | |
padding = 50; | |
var pressedKeys = [], | |
kkeys = []; | |
var translate = [0, 0]; | |
var highlightActive = false; | |
var pointed; // data element currently under the mouse pointer | |
var keyMap = { | |
16: ['shift', function(){}], // use with mouse click | |
17: ['ctrl', function(){}], // use with mouse click | |
18: ['alt', function(){}], // use with mouse click | |
27: ['esc', function() { | |
location.reload(true); | |
}], | |
36: ['home', function() { | |
translate = [0, 0]; | |
move(0, 0); | |
}], | |
37: ['arrow left', function() { | |
move(size, 0); | |
}], | |
38: ['arrow up', function() { | |
move(0, size); | |
}], | |
39: ['arrow right', function() { | |
move(-size, 0); | |
}], | |
40: ['arrow down', function() { | |
move(0, -size); | |
}], | |
72: ['h', function() { | |
highlightActive = !highlightActive; | |
toggleHighlight(highlightActive); | |
}], // use with mouse pointer | |
88: ['x', removeClicked], | |
90: ['z', tilt] | |
}; | |
var entityPoints = { | |
bomber: [[2,0], [3,0], [3,1], [4,1], [4,3], [5,3], [5,4], [4,4], [4,5], [3,5], [3,4], [2,4], [2,5], [1,5], [1,4], [0,4], [0,3], [1,3], [1,1], [2,1]], | |
fighter: [[2,0], [3,0], [3,2], [4,2], [4,3], [5,3], [5,4], [4,4], [4,5], [3,5], [3,4], [2,4], [2,5], [1,5], [1,4], [0,4], [0,3], [1,3], [1,2], [2,2]], | |
probe: [[2,1], [3,1], [3,3], [4,3], [4,4], [3,4], [3,5], [2,5], [2,4], [1,4], [1,3], [2,3]], | |
scout: [[2,0], [3,0], [3,2], [4,2], [4,4], [3,4], [3,5], [2,5], [2,4], [1,4], [1,2], [2,2]], | |
transport: [[1,0], [4,0], [4,5], [3,5], [3,4], [2,4], [2,5], [1,5]], | |
worker: [[1,2], [2,2], [2,1], [3,1], [3,2], [4,2], [4,5], [3,5], [3,4], [2,4], [2,5], [1,5]] | |
}; | |
function initialize() { | |
svg = d3.select('body') | |
.append('svg:svg') | |
.attr('width', w) | |
.attr('height', h) | |
.attr('pointer-events', 'all'); | |
// binding events over 'svg' only | |
svg.on('mousedown', mouseDrag) | |
.on('mousewheel', mouseScroll) // webkit | |
.on('DOMMouseScroll', mouseScroll); // firefox | |
svg.append('svg:rect') | |
.classed('bounds', true) | |
.attr('x', padding) | |
.attr('y', padding) | |
.attr('width', w - padding * 2) | |
.attr('height', h - padding * 2); | |
// crosshair | |
svg.append('svg:circle') | |
.classed('bounds', true) | |
.attr('cx', w / 2) | |
.attr('cy', h / 2) | |
.attr('r', 2); | |
tooltip = d3.select('body') | |
.append('div') | |
.classed('tooltip', true); | |
fillMap(); | |
retrieveData(); | |
position(); | |
render(); | |
//appendShip(svg, 'fighter', 2, -1, [135,70]); | |
//appendShip(svg, 'bomber', 2, -1, [170,70]); | |
//appendShip(svg, 'scout', 2, -1, [205,70]); | |
//appendShip(svg, 'probe', 2, -1, [240,70]); | |
//appendShip(svg, 'worker', 2, -1, [275,70]); | |
//appendShip(svg, 'transport', 2, -1, [310,70]); | |
} | |
function fillMap() { | |
var id = 0, | |
limit1 = 0, | |
limit2 = radius; | |
for (var j = -radius; j <= radius; j++) { | |
var i = limit1; | |
while (i <= limit2) { | |
data.push({ | |
id: id++, | |
coordinates: [i, j], | |
cost: 1, | |
lastSelected: 0, | |
type: 'regular', | |
resource: Math.random() < .005 | |
}); | |
i++; | |
} | |
if (j < 0) { | |
limit1--; | |
} else { | |
limit2--; | |
} | |
} | |
} | |
function retrieveData() { | |
/* | |
d3.json('http://hperantunes01.appspot.com/data', function(json) { | |
json.map(function(obj) { | |
d = data[obj.id]; | |
for(var i in obj) { | |
if (obj.hasOwnProperty(i) && i != id) { | |
d[i] = obj[i]; | |
} | |
} | |
}); | |
render(); // again | |
}); | |
*/ | |
} | |
function position() { | |
// http://goo.gl/8djhT | |
var stepX = tilted ? size * 3 / 4 : Math.sqrt(3) * size / 2, | |
stepY = tilted ? Math.sqrt(3) * size / 2 : size * 3 / 4, | |
offset = size / Math.sqrt(3) * 3 / 4 | |
data.map(function(d) { | |
var i = d.coordinates[0], | |
j = d.coordinates[1], | |
x = stepX * i + (!tilted ? offset * j : 0) + w / 2, | |
y = stepY * j + (tilted ? offset * i : 0) + h / 2; | |
d.centroid = [Math.round(x * 1e2) / 1e2, Math.round(y * 1e2) / 1e2]; | |
d.visible = !outbounds(x, y); | |
}); | |
} | |
function insertEntity(hexId) { | |
if (entities.filter(function(e) { return e.hexId == hexId; }).length) { | |
return; | |
} | |
entities.push({ | |
id: entities.length, | |
hexId: hexId, | |
visible: true | |
}); | |
moveEntities(); | |
} | |
function render() { | |
renderMap(); | |
renderEntities(); | |
} | |
function renderMap() { | |
var grid = svg.selectAll('polygon.tile') | |
.data(getVisibleData(), function(d) { return d.id; }); | |
grid.enter() | |
.sort(function(a, b) { return a.id - b.id; }) | |
.append('svg:polygon') | |
.classed('tile', true) | |
.classed('selected', function(d) { return !~(d.type.search('r')); }) | |
.classed('resource', function(d) { return d.resource; }) | |
.attr('points', function(d) { | |
return hex(d.centroid, size, tilted).join(' '); | |
}) | |
.on('mouseover', mouseOver) | |
.on('mousemove', mouseMove) | |
.on('mouseout', mouseOut) | |
.on('mousedown', click); | |
grid.exit().remove(); | |
} | |
function renderEntities() { | |
var elements = svg.selectAll('rect.entity') | |
.data( | |
entities.filter(function(d) { return d.visible; }), | |
function(d) { return d.id; } | |
); | |
elements.enter() | |
.sort(function(a, b) { return a.id - b.id; }) | |
.append('svg:rect') | |
.classed('entity', true) | |
.attr('x', function(d) { return data[d.hexId].centroid[0] - size / 6; }) | |
.attr('y', function(d) { return data[d.hexId].centroid[1] - size / 6; }) | |
.attr('width', size / 3) | |
.attr('height', size / 3); | |
elements.exit().remove(); | |
} | |
// Custom drag behavior (replacing 'zoom') | |
function mouseDrag() { | |
var m0 = d3.svg.mouse(this), | |
that = this, | |
previousMove = [0, 0]; | |
d3.select('body').on('mousemove', function() { | |
var m1 = d3.svg.mouse(that), | |
shift = d3.event.shiftKey, | |
ctrl = d3.event.ctrlKey, | |
alt = d3.event.altKey, | |
x = m1[0] - m0[0] - previousMove[0], | |
y = m1[1] - m0[1] - previousMove[1]; | |
if (!(shift || ctrl || alt)) { | |
move(x, y); | |
} | |
previousMove[0] += x; | |
previousMove[1] += y; | |
}); | |
d3.select('body').on('mouseup', function() { | |
d3.select('body') | |
.on('mousemove', null) | |
.on('mouseup', null); | |
}); | |
d3.event.preventDefault(); | |
} | |
// | |
function move(x, y) { | |
translate[0] += x; | |
translate[1] += y; | |
moveMap(); | |
moveEntities(); | |
} | |
function moveMap() { | |
var dx = translate[0], | |
dy = translate[1]; | |
// Update data | |
data.filter(function(d) { | |
var x = d.centroid[0] + dx, | |
y = d.centroid[1] + dy; | |
return d.visible && outbounds(x, y); | |
}).map(function(d) { | |
d.visible = false; | |
return d; | |
}); | |
data.filter(function(d) { | |
var x = d.centroid[0] + dx, | |
y = d.centroid[1] + dy; | |
return !d.visible && !outbounds(x, y); | |
}).map(function(d) { | |
d.visible = true; | |
return d; | |
}); | |
// | |
renderMap(); | |
svg.selectAll('.tile') | |
.attr('transform', 'translate(' + [dx, dy] + ')'); | |
} | |
function moveEntities() { | |
var dx = translate[0], | |
dy = translate[1]; | |
// Update entities | |
entities.filter(function(d) { | |
var x = data[d.hexId].centroid[0] + dx, | |
y = data[d.hexId].centroid[1] + dy; | |
return d.visible && outbounds(x, y); | |
}).map(function(d) { | |
d.visible = false; | |
return d; | |
}); | |
entities.filter(function(d) { | |
var x = data[d.hexId].centroid[0] + dx, | |
y = data[d.hexId].centroid[1] + dy; | |
return !d.visible && !outbounds(x, y); | |
}).map(function(d) { | |
d.visible = true; | |
return d; | |
}); | |
// | |
renderEntities(); | |
svg.selectAll('.entity') | |
.attr('transform', function(d) { | |
return 'translate(' + [dx, dy] + ')'; | |
}); | |
} | |
function outbounds(x, y) { | |
return x < padding || x > w - padding || y < padding || y > h - padding; | |
} | |
function removeAll() { | |
svg.selectAll('.tile, .entity').remove(); | |
} | |
function removeClicked() { | |
svg.selectAll('.tile') | |
.filter(function(d) { | |
return d.lastSelected; | |
}) | |
.remove(); | |
} | |
function neighbors(id, distance, visible) { | |
var x1 = data[id].coordinates[0], | |
y1 = data[id].coordinates[1]; | |
return (visible ? getVisibleData() : data).filter(function(d) { | |
var x2 = d.coordinates[0], | |
y2 = d.coordinates[1], | |
dx = x2 - x1, | |
dy = y2 - y1; | |
return (dx * dy < 0 ? Math.max(Math.abs(dx), Math.abs(dy)) : Math.abs(dx) + Math.abs(dy)) <= distance; | |
}).filter(function(d) { return d.id != id; }); | |
} | |
function hex(centroid) { | |
var a = size / 2, | |
b = (Math.sqrt(3) * a) / 2, | |
x = centroid[0], | |
y = centroid[1]; | |
return tilted | |
? [[x - a / 2, y - b], [x - a, y], [x - a / 2, y + b], [x + a / 2, y + b], [x + a, y], [x + a / 2, y - b]] | |
: [[x - b, y - a / 2], [x - b, y + a / 2], [x, y + a], [x + b, y + a / 2], [x + b, y - a / 2], [x, y - a]]; | |
} | |
// document events | |
function isPressed(key) { | |
for(var i in keyMap) { | |
if (keyMap.hasOwnProperty(i) && keyMap[i][0] === key) { | |
return !!~pressedKeys.indexOf(+i); | |
} | |
} | |
} | |
function keyDown(e) { | |
var keyCode = e.keyCode ? e.keyCode : e.which; | |
if (!~pressedKeys.indexOf(keyCode)) { | |
pressedKeys.push(keyCode); | |
} | |
if (keyCode in keyMap) { | |
keyMap[keyCode][1].call(); | |
e.preventDefault(); | |
} | |
console.log(pressedKeys); | |
} | |
function keyUp(e) { | |
var keyCode = e.keyCode ? e.keyCode : e.which; | |
if (!!~pressedKeys.indexOf(keyCode)) { | |
pressedKeys = pressedKeys.filter(function(e) { return e != keyCode; }); | |
} | |
if (keyCode in keyMap && keyMap[keyCode][2]) { | |
keyMap[keyCode][2].call(); | |
e.preventDefault(); | |
} | |
console.log(pressedKeys); | |
} | |
function kCheck(e) { | |
kkeys.push(e.keyCode ? e.keyCode : e.which); | |
if (kkeys.length < 11) { | |
return; | |
} else if (kkeys.length == 11 && kkeys.toString() == '38,38,40,40,37,39,37,39,66,65,13') { | |
window.location = 'http://goo.gl/D2lOn'; | |
} else { | |
document.removeEventListener('keyup', kCheck, false); | |
kkeys = undefined; | |
} | |
} | |
// | |
// d3 mouse events | |
function mouseOver(d, i) { | |
pointed = d; | |
if (highlightActive) { | |
toggleHighlight(true); | |
} | |
d3.select(this).classed('over', true); | |
tooltip.text(d.id + ' (' + d.coordinates + ') / ' + d.lastSelected) | |
.classed('visible', true); | |
} | |
function mouseMove(d, i) { | |
tooltip.style('top', (d3.event.pageY - 20) + 'px') | |
.style('left', (d3.event.pageX + 20) + 'px'); | |
} | |
function mouseOut(d, i) { | |
if (highlightActive) { | |
toggleHighlight(false); | |
} | |
pointed = undefined; | |
d3.select(this).classed('over', false); | |
tooltip.classed('visible', false); | |
} | |
function click(d, i) { | |
var element = d3.select(this), | |
shift = d3.event.shiftKey, | |
ctrl = d3.event.ctrlKey, | |
alt = d3.event.altKey; | |
if (shift) { | |
insertEntity(d.id); | |
} | |
if (ctrl) { // move this to an external function | |
var selected = element.classed('selected'); | |
element.classed('selected', !selected); | |
d.type = selected ? 'regular' : 'selected'; | |
d.lastSelected = +d3.event.timeStamp; | |
tooltip.text(d.id + ' (' + d.coordinates + ') / ' + d.lastSelected); | |
} | |
console.log('click', d/*, this.getScreenCTM()*/); | |
} | |
var test = [0, 0]; | |
function mouseScroll(d, i) { | |
test = d3.svg.mouse(this); | |
var e = d3.event, | |
delta = e.wheelDelta ? e.wheelDelta : e.detail ? -e.detail : 0; | |
if (delta > 0) { | |
scrollUp(); | |
} else { | |
scrollDown(); | |
} | |
d3.event.preventDefault(); | |
} | |
// | |
function scrollDown() { | |
if (size > 20) { | |
zoom(-20); // zoom out | |
} | |
} | |
function scrollUp() { | |
if (size < 80) { | |
zoom(20); // zoom in | |
} | |
} | |
function zoom(amount) { | |
var a = test[0] - w / 2, | |
b = test[1] - h / 2; | |
console.log(a, b); | |
var proportion = (size + amount) / size, | |
dx = translate[0] * proportion - translate[0], | |
dy = translate[1] * proportion - translate[1]; | |
size += amount; | |
removeAll(); | |
position(); | |
move(dx, dy); | |
} | |
function tilt() { | |
tilted = !tilted; | |
removeAll(); | |
position(); | |
move(0, 0); | |
} | |
function toggleHighlight(set) { | |
if (!pointed) { | |
return; | |
} | |
var elements = svg.selectAll('.tile').data( | |
neighbors(pointed.id, 5), | |
function(d) { return d.id; } | |
); | |
elements.classed('neighbor', set && !elements.classed('neighbor')); | |
} | |
function totalTiles() { | |
//return data.length + 1; | |
return 3 * Math.pow(radius + 1, 2) - 3 * (radius + 1) + 1; | |
} | |
function getEntity(type, size) { | |
return entityPoints.hasOwnProperty(type) | |
? entityPoints[type].map(function(d) { return [(d[0] * 2 - 5) * size, (d[1] * 2 - 5) * size]; }) | |
: []; | |
} | |
function appendShip(svg, type, size, allegiance, translate) { | |
svg.append('svg:polygon') | |
.classed('entity', true) | |
.classed('ship', true) | |
.classed('own', allegiance) | |
.classed('enemy', !~allegiance) | |
.attr('points', function(d) { | |
return getEntity(type, size).join(' '); | |
}) | |
.attr('transform', 'translate(' + translate + ')'); | |
} | |
})(); | |
function getVisibleData() { | |
return data.filter(function(d) { return d.visible; }); | |
} | |
function getStyleSheets() { // test | |
return document.styleSheets[1].rules[1].selectorText; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment