mirror of
https://github.com/vcmi/vcmi.git
synced 2025-09-16 09:26:28 +02:00
Merge pull request #3688 from vcmi/fix_penrose_tiling
Fix penrose tiling
This commit is contained in:
@@ -171,6 +171,7 @@ CMap::CMap(IGameCallback * cb)
|
|||||||
, checksum(0)
|
, checksum(0)
|
||||||
, grailPos(-1, -1, -1)
|
, grailPos(-1, -1, -1)
|
||||||
, grailRadius(0)
|
, grailRadius(0)
|
||||||
|
, waterMap(false)
|
||||||
, uidCounter(0)
|
, uidCounter(0)
|
||||||
{
|
{
|
||||||
allHeroes.resize(VLC->heroh->size());
|
allHeroes.resize(VLC->heroh->size());
|
||||||
@@ -605,6 +606,10 @@ bool CMap::calculateWaterContent()
|
|||||||
{
|
{
|
||||||
waterMap = true;
|
waterMap = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
waterMap = false;
|
||||||
|
}
|
||||||
|
|
||||||
return waterMap;
|
return waterMap;
|
||||||
}
|
}
|
||||||
|
@@ -873,6 +873,11 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
int levels = map.levels();
|
int levels = map.levels();
|
||||||
|
|
||||||
// Find current center of mass for each zone. Move zone to that center to balance zones sizes
|
// Find current center of mass for each zone. Move zone to that center to balance zones sizes
|
||||||
|
std::vector<RmgMap::Zones> zonesOnLevel;
|
||||||
|
for(int level = 0; level < levels; level++)
|
||||||
|
{
|
||||||
|
zonesOnLevel.push_back(map.getZonesOnLevel(level));
|
||||||
|
}
|
||||||
|
|
||||||
int3 pos;
|
int3 pos;
|
||||||
|
|
||||||
@@ -883,12 +888,9 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
for(pos.y = 0; pos.y < height; pos.y++)
|
for(pos.y = 0; pos.y < height; pos.y++)
|
||||||
{
|
{
|
||||||
distances.clear();
|
distances.clear();
|
||||||
for(const auto & zone : zones)
|
for(const auto & zone : zonesOnLevel[pos.z])
|
||||||
{
|
{
|
||||||
if (zone.second->getPos().z == pos.z)
|
distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
|
||||||
distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
|
|
||||||
else
|
|
||||||
distances.emplace_back(zone.second, std::numeric_limits<float>::max());
|
|
||||||
}
|
}
|
||||||
boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone
|
boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone
|
||||||
}
|
}
|
||||||
@@ -906,27 +908,30 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
for(const auto & zone : zones)
|
for(const auto & zone : zones)
|
||||||
zone.second->clearTiles(); //now populate them again
|
zone.second->clearTiles(); //now populate them again
|
||||||
|
|
||||||
// Assign zones to closest Penrose vertex
|
|
||||||
PenroseTiling penrose;
|
PenroseTiling penrose;
|
||||||
auto vertices = penrose.generatePenroseTiling(zones.size() / map.levels(), rand);
|
for (int level = 0; level < levels; level++)
|
||||||
|
|
||||||
std::map<std::shared_ptr<Zone>, std::set<int3>> vertexMapping;
|
|
||||||
|
|
||||||
for (const auto & vertex : vertices)
|
|
||||||
{
|
{
|
||||||
distances.clear();
|
//Create different tiling for each level
|
||||||
for(const auto & zone : zones)
|
|
||||||
|
auto vertices = penrose.generatePenroseTiling(zonesOnLevel[level].size(), rand);
|
||||||
|
|
||||||
|
// Assign zones to closest Penrose vertex
|
||||||
|
std::map<std::shared_ptr<Zone>, std::set<int3>> vertexMapping;
|
||||||
|
|
||||||
|
for (const auto & vertex : vertices)
|
||||||
{
|
{
|
||||||
distances.emplace_back(zone.second, zone.second->getCenter().dist2dSQ(float3(vertex.x(), vertex.y(), 0)));
|
distances.clear();
|
||||||
|
for(const auto & zone : zonesOnLevel[level])
|
||||||
|
{
|
||||||
|
distances.emplace_back(zone.second, zone.second->getCenter().dist2dSQ(float3(vertex.x(), vertex.y(), level)));
|
||||||
|
}
|
||||||
|
auto closestZone = boost::min_element(distances, compareByDistance)->first;
|
||||||
|
|
||||||
|
vertexMapping[closestZone].insert(int3(vertex.x() * width, vertex.y() * height, level)); //Closest vertex belongs to zone
|
||||||
}
|
}
|
||||||
auto closestZone = boost::min_element(distances, compareByDistance)->first;
|
|
||||||
|
|
||||||
vertexMapping[closestZone].insert(int3(vertex.x() * width, vertex.y() * height, closestZone->getPos().z)); //Closest vertex belongs to zone
|
//Assign actual tiles to each zone
|
||||||
}
|
pos.z = level;
|
||||||
|
|
||||||
//Assign actual tiles to each zone
|
|
||||||
for (pos.z = 0; pos.z < levels; pos.z++)
|
|
||||||
{
|
|
||||||
for (pos.x = 0; pos.x < width; pos.x++)
|
for (pos.x = 0; pos.x < width; pos.x++)
|
||||||
{
|
{
|
||||||
for (pos.y = 0; pos.y < height; pos.y++)
|
for (pos.y = 0; pos.y < height; pos.y++)
|
||||||
@@ -937,27 +942,35 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
auto zone = zoneVertex.first;
|
auto zone = zoneVertex.first;
|
||||||
for (const auto & vertex : zoneVertex.second)
|
for (const auto & vertex : zoneVertex.second)
|
||||||
{
|
{
|
||||||
if (zone->getCenter().z == pos.z)
|
distances.emplace_back(zone, metric(pos, vertex));
|
||||||
distances.emplace_back(zone, metric(pos, vertex));
|
|
||||||
else
|
|
||||||
distances.emplace_back(zone, std::numeric_limits<float>::max());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Tile closes to vertex belongs to zone
|
//Tile closest to vertex belongs to zone
|
||||||
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
|
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
|
||||||
closestZone->area().add(pos);
|
closestZone->area().add(pos);
|
||||||
map.setZoneID(pos, closestZone->getId());
|
map.setZoneID(pos, closestZone->getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(const auto & zone : zonesOnLevel[level])
|
||||||
|
{
|
||||||
|
if(zone.second->area().empty())
|
||||||
|
{
|
||||||
|
// FIXME: Some vertices are duplicated, but it's not a source of problem
|
||||||
|
logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString());
|
||||||
|
for (const auto & vertex : vertices)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Penrose Vertex: %s", vertex.toString());
|
||||||
|
}
|
||||||
|
throw rmgException("Empty zone after Penrose tiling");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//set position (town position) to center of mass of irregular zone
|
//set position (town position) to center of mass of irregular zone
|
||||||
for(const auto & zone : zones)
|
for(const auto & zone : zones)
|
||||||
{
|
{
|
||||||
if(zone.second->area().empty())
|
|
||||||
throw rmgException("Empty zone after Penrose tiling");
|
|
||||||
|
|
||||||
moveZoneToCenterOfMass(zone.second);
|
moveZoneToCenterOfMass(zone.second);
|
||||||
|
|
||||||
//TODO: similiar for islands
|
//TODO: similiar for islands
|
||||||
|
@@ -38,6 +38,16 @@ bool Point2D::operator < (const Point2D& other) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Point2D::toString() const
|
||||||
|
{
|
||||||
|
//Performance is important here
|
||||||
|
std::string result = "(" +
|
||||||
|
std::to_string(this->x()) + " " +
|
||||||
|
std::to_string(this->y()) + ")";
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Triangle::Triangle(bool t_123, const TIndices & inds):
|
Triangle::Triangle(bool t_123, const TIndices & inds):
|
||||||
tiling(t_123),
|
tiling(t_123),
|
||||||
indices(inds)
|
indices(inds)
|
||||||
@@ -57,14 +67,14 @@ Triangle::~Triangle()
|
|||||||
|
|
||||||
Point2D Point2D::rotated(float radians) const
|
Point2D Point2D::rotated(float radians) const
|
||||||
{
|
{
|
||||||
float cosAngle = cos(radians);
|
float cosAngle = cos(radians);
|
||||||
float sinAngle = sin(radians);
|
float sinAngle = sin(radians);
|
||||||
|
|
||||||
// Apply rotation matrix transformation
|
// Apply rotation matrix transformation
|
||||||
float newX = x() * cosAngle - y() * sinAngle;
|
float newX = x() * cosAngle - y() * sinAngle;
|
||||||
float newY = x() * sinAngle + y() * cosAngle;
|
float newY = x() * sinAngle + y() * cosAngle;
|
||||||
|
|
||||||
return Point2D(newX, newY);
|
return Point2D(newX, newY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PenroseTiling::split(Triangle& p, std::vector<Point2D>& points,
|
void PenroseTiling::split(Triangle& p, std::vector<Point2D>& points,
|
||||||
|
@@ -32,6 +32,8 @@ public:
|
|||||||
Point2D rotated(float radians) const;
|
Point2D rotated(float radians) const;
|
||||||
|
|
||||||
bool operator < (const Point2D& other) const;
|
bool operator < (const Point2D& other) const;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Point2D rotatePoint(const Point2D& point, double radians, const Point2D& origin);
|
Point2D rotatePoint(const Point2D& point, double radians, const Point2D& origin);
|
||||||
|
@@ -239,6 +239,19 @@ RmgMap::Zones & RmgMap::getZones()
|
|||||||
return zones;
|
return zones;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RmgMap::Zones RmgMap::getZonesOnLevel(int level) const
|
||||||
|
{
|
||||||
|
Zones zonesOnLevel;
|
||||||
|
for(const auto& zonePair : zones)
|
||||||
|
{
|
||||||
|
if(zonePair.second->isUnderground() == (bool)level)
|
||||||
|
{
|
||||||
|
zonesOnLevel.insert(zonePair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zonesOnLevel;
|
||||||
|
}
|
||||||
|
|
||||||
bool RmgMap::isBlocked(const int3 &tile) const
|
bool RmgMap::isBlocked(const int3 &tile) const
|
||||||
{
|
{
|
||||||
assertOnMap(tile);
|
assertOnMap(tile);
|
||||||
|
@@ -74,6 +74,7 @@ public:
|
|||||||
using ZoneVector = std::vector<ZonePair>;
|
using ZoneVector = std::vector<ZonePair>;
|
||||||
|
|
||||||
Zones & getZones();
|
Zones & getZones();
|
||||||
|
Zones getZonesOnLevel(int level) const;
|
||||||
|
|
||||||
void registerZone(FactionID faction);
|
void registerZone(FactionID faction);
|
||||||
ui32 getZoneCount(FactionID faction);
|
ui32 getZoneCount(FactionID faction);
|
||||||
|
Reference in New Issue
Block a user