Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
atom.declare( 'LibCanvas.Engines.Hex.Projection', {
multipliers: {
height: Math.cos( Math.PI / 6 ) * 2,
chord : 1/2 // Math.sin( Math.PI / 6 )
},
initialize: function (settings) {
settings = this.settings = new Settings({
baseLength : 0,
chordLength: null,
hexHeight : null,
start : new Point(0, 0)
}).set(settings);
if (settings.get('chordLength') == null) {
settings.set({
chordLength: settings.get('baseLength') * this.multipliers.chord,
hexHeight : settings.get('hexHeight' ) * this.multipliers.height
});
}
},
isZero: function (c) {
return c[0] === 0 && c[1] === 0 && c[2] === 0;
},
rgbToPoint: function (coordinates) {
var
red = coordinates[0],
green = coordinates[1],
blue = coordinates[2],
settings = this.settings,
base = settings.get('baseLength'),
chord = settings.get('chordLength'),
height = settings.get('hexHeight'),
start = settings.get('start');
if (red + green + blue !== 0) {
throw new Error( 'Wrong coordinates: ' + red + ' ' + green + ' ' + blue);
}
return new Point(
start.x + (base + chord) * red,
start.y + (blue - green) * height / 2
);
},
pointToRgb: function (point) {
var
settings = this.settings,
base = settings.get('baseLength'),
chord = settings.get('chordLength'),
height = settings.get('hexHeight'),
start = settings.get('start'),
// counting coords
red = (point.x - start.x) / (base + chord),
blue = (point.y - start.y - red * height / 2) / height,
green = 0 - red - blue;
var dist = function (c) {
return Math.abs(c[0] - red) + Math.abs(c[1] - green) + Math.abs(c[2] - blue);
};
var
rF = Math.floor(red ), rC = Math.ceil(red ),
gF = Math.floor(green), gC = Math.ceil(green),
bF = Math.floor(blue ), bC = Math.ceil(blue );
return [
// we need to find closest integer coordinates
[rF, gF, bF],
[rF, gC, bF],
[rF, gF, bC],
[rF, gC, bC],
[rC, gF, bF],
[rC, gC, bF],
[rC, gF, bC],
[rC, gC, bC]
].filter(function (v) {
// only correct variants - sum must be equals to zero
return atom.array.sum(v) == 0;
})
.sort(function (left, right) {
// we need coordinates with the smallest distance
return dist(left) < dist(right) ? -1 : 1;
})[0];
},
createPolygon: function (center) {
var
settings = this.settings,
halfBase = settings.get('baseLength') / 2,
halfHeight = settings.get('hexHeight') / 2,
radius = halfBase + settings.get('chordLength'),
right = center.x + halfBase,
left = center.x - halfBase,
top = center.y - halfHeight,
bottom = center.y + halfHeight;
return new Polygon([
new Point(left , top), // top-left
new Point(right, top), // top-right
new Point(center.x + radius, center.y), // right
new Point(right, bottom), // bottom-right
new Point(left , bottom), // bottom-left
new Point(center.x - radius, center.y) // left
]);
}
});здесь же на одно преобразивание уходит целых 5 операций деления
Разбивка города на 6-угольные кварталы неудобна для лошадей — улицы будут состоять из сплошных поворотов, с ветерком не прокатишься.
Для зданий шестиугольник вообще ни к чему: там главный приоритет — не затраты материала на стены, а площадь окон в пересчете на объем, чем больше — тем лучше.
Кроме того, вдоль стены прямоугольного склада/фургона можно уложить без щелей прямоугольные контейнеры любого размера. Шестиугольные так не уложишь.
Далее, прямоугольник можно двигать по проходу, ширина которого равна ширине прямоугольника (проделанному прямо в решетке их укладки), а для 6-угольника придется освобождать гораздо больше места. И в жизни мы этим пользуемся очень часто. В общем, прямоугольники лучше, и в первую очередь — из-за того, что 90+90=180.
Вполне прокатишься — не обязательно же на каждом повороте куда-то заворачивать. Для перемещения из точки A в точку B нужен всего один поворот максимум, как в случае и с прямоугольниками. Ну и тем более на лошадях сейчас почти никто не катается.
Но у шестиугольников есть другие преимущества
Если я не поверну, то на первом же перекрестке (там сходятся три улицы под углом 120 гр, как и на любом другом) врежусь в угол квартала, находящегося передо мной. И не важно, на лошади я буду, на автомобиле или на гексацикле.
Ну а прямоугольные можно так уложить :)
double along_coord = m_period * m + (l&1 ? m_period/2. : 0.);
// Считаем проекции на оси m, l и r (в половинах периода решетки).
// Здесь можно было обойтись меньшим количеством операций,
// засунув, например, двойку и период решетки под косинусы и синусы
// в отдельные константы
// int m = floor(2 * along_coord / m_period); // это уже не нужно
int l = floor(2 * (cos60 * along_coord + sin60 * other_coord) / m_period);
int r = floor(2 * (cos60 * along_coord - sin60 * other_coord) / m_period);
// И, собственно, сам финт ушами:
int rc = floor((l - r + 1) / 3.0);
int cc = floor((along_coord + (!(rc&1) ? m_period/2. : 0.)) / m_period);
return MyPointI(cc, rc);
О прямоугольных координатах и гексагональных сетках