Skip to content

Instantly share code, notes, and snippets.

@hperantunes
Last active April 11, 2023 01:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hperantunes/1100163 to your computer and use it in GitHub Desktop.
Save hperantunes/1100163 to your computer and use it in GitHub Desktop.
hexagons test
<!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