Génération simplifiée, avec détection des motifs produits.

This commit is contained in:
Georges Dupéron 2011-12-10 20:55:23 +01:00
parent 9d8a85afd6
commit 2db9bb222a
18 changed files with 158 additions and 289 deletions

View File

@ -4,7 +4,7 @@ CCWARN=-Wall -Wextra -Werror
# -flto (nécessite GCC 4.5) -m32 ou -m64
CFLAGS=-O0 -I. $(CCWARN)
OBJECTS = main.o view.o hash.o segment.o vertex.o triangle.o rules/chose.o rules/rectangleroutes.o rules/quad.o rules/quadroutes.o rules/route.o rules/carrefour.o rules/batiment.o
SOURCES = main.cpp view.cpp hash.cpp vertex.cpp segment.cpp triangle.cpp quad.cpp rules/chose.cpp rules/quadrilatere.cpp rules/quadcroix.cpp rules/route.cpp rules/carrefour.cpp rules/batiment.cpp
LIBS = -lm -lGL -lGLU -lSDL -lGLEW
EXECUTABLE = city
@ -16,9 +16,9 @@ all: $(EXECUTABLE)
clean:
rm -f $(EXECUTABLE) all_includes.hh.d all_includes.hh.gch all.cpp
$(EXECUTABLE): $(OBJECTS:.o=.cpp) all_includes.hh.gch Makefile
$(EXECUTABLE): $(SOURCES) all_includes.hh.gch Makefile
@:> all.cpp
@$(foreach FILE,$(OBJECTS:.o=.cpp),echo '#include "'"$(FILE)"'"' >> all.cpp;)
@$(foreach FILE,$(SOURCES),echo '#include "'"$(FILE)"'"' >> all.cpp;)
$(CXX) $(LIBS) $(CFLAGS) all.cpp -o $@
@rm all.cpp

View File

@ -15,19 +15,20 @@ class Chose;
#include <GL/glu.h>
#include <GL/gl.h>
#include "directions.hh"
#include "vertex.hh"
#include "segment.hh"
#include "triangle.hh"
#include "directions.hh"
#include "quad.hh"
#include "hash.hh"
#include "view.hh"
#include "rules/chose.hh"
#include "rules/quad.hh"
#include "rules/batiment.hh"
#include "rules/carrefour.hh"
#include "rules/route.hh"
#include "rules/rectangleroutes.hh"
#include "rules/quadroutes.hh"
#include "rules/quadrilatere.hh"
#include "rules/quadcroix.hh"
#endif

View File

@ -1,18 +1,26 @@
#ifndef _DIRECTIONS_HH_
#define _DIRECTIONS_HH_
typedef enum Cardinal {
enum Cardinal {
N = 0,
E = 1,
S = 2,
W = 3
} Cardinal;
};
typedef enum Coin {
inline Cardinal operator+(Cardinal c, int i) {
return Cardinal((int(c) + int(i)) & 3);
}
enum Coin {
NE = 0,
SE = 1,
SW = 2,
NW = 3
} Coin;
};
inline Coin operator+(Coin c, int i) {
return Coin((int(c) + int(i)) & 3);
}
#endif

View File

@ -1,10 +1,14 @@
#include "all_includes.hh"
// TODO : split bâtiment en faces, puis en triangles.
// TODO : probabilités des différents types de bâtiments.
// TODO : midpoint displacement sur les probabilités des différents types de bâtiments.
// TODO : largeur des routes : ???
// Quadrilatere(corner[4])
// -> croix de routes
// -> bâtiment
// -> bâtiment dans le "bout" le plus "étroit", et lignes dans une seule direction dans le reste.
void recursiveSubdivide(Chose* c) {
if (c->subdivide()) {
std::vector<Chose*>::iterator it;
@ -21,8 +25,8 @@ int main() {
Vertex ne(size, size, 0);
Vertex se(size, 0, 0);
Vertex sw(0, 0, 0);
Vertex nw(0, size*1.3, 0);
Chose* c = new QuadRoutes(ne,se,sw,nw);
Vertex nw(0, size, 0);
Chose* c = Quadrilatere::factory(ne,se,sw,nw);
// c->subdivide();
recursiveSubdivide(c);

16
quad.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "all_includes.hh"
Quad::Quad() {}
Quad::Quad(Vertex ne, Vertex se, Vertex sw, Vertex nw) {
corner[NE] = ne;
corner[SE] = se;
corner[SW] = sw;
corner[NW] = nw;
}
void Quad::offset(/*Cardinal*/int side, int offset) {
Vertex voffset = (corner[NE + side]-corner[NW + side]).perpendicular().setNorm(offset);
corner[NE + side] = corner[NE + side] + voffset.projectOn(corner[NE + side]-corner[SE + side]);
corner[NW + side] = corner[NW + side] + voffset.projectOn(corner[NW + side]-corner[SW + side]);
}

17
quad.hh Normal file
View File

@ -0,0 +1,17 @@
#ifndef _QUAD_HH_
#define _QUAD_HH_
#include "all_includes.hh"
// Quad est un quadrilatère
class Quad {
public:
Vertex corner[4];
public:
Quad();
Quad(Vertex ne, Vertex se, Vertex sw, Vertex nw);
void offset(/*Cardinal*/int side, int offset);
};
#endif

View File

@ -1,31 +0,0 @@
#include "all_includes.hh"
Quad::Quad(Vertex ne, Vertex se, Vertex sw, Vertex nw) : Chose(), ne(ne), se(se), sw(sw), nw(nw) {
addEntropy(ne,se,sw,nw);
// triangulation();
}
Vertex& Quad::corner(Coin corner, int rotation) {
switch ((corner + rotation) & 3) {
case NE: return ne;
case SE: return se;
case SW: return sw;
default: return nw;
}
}
void Quad::offset(/*Cardinal*/int side, int offset) {
Vertex voffset = (corner(NE,side)-corner(NW,side)).perpendicular().setNorm(offset);
corner(NE,side) = corner(NE,side) + voffset.projectOn(corner(NE,side)-corner(SE,side));
corner(NW,side) = corner(NW,side) + voffset.projectOn(corner(NW,side)-corner(SW,side));
}
bool Quad::subdivide() {
return false;
}
void Quad::triangulation() {
triangles.reserve(2);
addTriangle(new Triangle(ne, nw, sw, 0xc0, 0xc0, 0xc0));
addTriangle(new Triangle(sw, se, ne, 0xc0, 0xc0, 0xc0));
}

View File

@ -1,22 +0,0 @@
#ifndef _RULES_QUAD_HH_
#define _RULES_QUAD_HH_
#include "all_includes.hh"
// Quad est un quadrilatère
class Quad : public Chose {
public:
Vertex ne;
Vertex se;
Vertex sw;
Vertex nw;
public:
Quad(Vertex ne, Vertex se, Vertex sw, Vertex nw);
virtual bool subdivide();
virtual void triangulation();
void offset(/*Cardinal*/int side, int n);
Vertex& corner(Coin corner, int rotation);
};
#endif

27
rules/quadcroix.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "all_includes.hh"
QuadCroix::QuadCroix(Vertex ne, Vertex se, Vertex sw, Vertex nw) : Quadrilatere(ne, se, sw, nw) {
}
bool QuadCroix::subdivide() {
Vertex middle[4];
Quad q[4];
Vertex cn = Segment(corner[NW], corner[NE]).randomPos(seed, -1, 25, 75);
Vertex cs = Segment(corner[SE], corner[SW]).randomPos(seed, -2, 25, 75);
Vertex c = Segment(cn, cs).randomPos(seed, -3, 25, 75);
for (int i = 0; i < 4; i++) {
middle[N+i] = Segment(corner[NW+i], corner[NE+i]).randomPos(seed, i, 25, 75);
}
for (int i = 0; i < 4; i++) {
q[i] = Quad(corner[NE+i], middle[E+i], c, middle[N+i]);
q[i].offset(W,-hrw); q[i].offset(S,-hrw);
}
addChild(new Carrefour(q[0].corner[SW], q[1].corner[SW], q[2].corner[SW], q[3].corner[SW]));
for (int i = 0; i < 4; i++) {
addChild(new Route(q[NE+i].corner[NW], q[NE+i].corner[SW], q[NW+i].corner[SW], q[NW+i].corner[SE]));
addChild(Quadrilatere::factory(q[i].corner[0], q[i].corner[1], q[i].corner[2], q[i].corner[3]));
}
return true;
}

16
rules/quadcroix.hh Normal file
View File

@ -0,0 +1,16 @@
#ifndef _RULES_QUAD_CROIX_HH_
#define _RULES_QUAD_CROIX_HH_
#include "all_includes.hh"
// Quad est un quadrilatère
class QuadCroix : public Quadrilatere {
private:
static const int hrw = 250; // half road width : 2,50m.
public:
QuadCroix(Vertex ne, Vertex se, Vertex sw, Vertex nw);
virtual bool subdivide();
};
#endif

32
rules/quadrilatere.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "all_includes.hh"
Quadrilatere::Quadrilatere(Vertex ne, Vertex se, Vertex sw, Vertex nw) : Chose() {
addEntropy(ne, se, sw, nw);
corner[NE] = ne;
corner[SE] = se;
corner[SW] = sw;
corner[NW] = nw;
triangulation();
}
Chose* Quadrilatere::factory(Vertex ne, Vertex se, Vertex sw, Vertex nw) {
if (Segment(ne,se).length() < 2500 ||
Segment(se,sw).length() < 2500 ||
Segment(sw,nw).length() < 2500 ||
Segment(nw,ne).length() < 2500) {
return new Batiment(ne, se, sw, nw);
} else {
return new QuadCroix(ne, se, sw, nw);
}
}
bool Quadrilatere::subdivide() {
return false;
}
void Quadrilatere::triangulation() {
triangles.reserve(2);
addTriangle(new Triangle(corner[NE], corner[NW], corner[SW], 0xc0, 0xc0, 0xc0));
addTriangle(new Triangle(corner[SW], corner[SE], corner[NE], 0xc0, 0xc0, 0xc0));
}

17
rules/quadrilatere.hh Normal file
View File

@ -0,0 +1,17 @@
#ifndef _RULES_QUADRILATERE_HH_
#define _RULES_QUADRILATERE_HH_
#include "all_includes.hh"
// RectangleRoutes est un quadrilatère de routes avec des angles aux coins égaux à 90°.
class Quadrilatere : public Chose {
public:
Vertex corner[4];
public:
Quadrilatere(Vertex ne, Vertex se, Vertex sw, Vertex nw);
virtual bool subdivide();
virtual void triangulation();
static Chose* factory(Vertex ne, Vertex se, Vertex sw, Vertex nw);
};
#endif

View File

@ -1,93 +0,0 @@
#include "all_includes.hh"
const float QuadRoutes::cosMin = std::cos((90+maxAngleDelta)/180.f*3.14159);
const float QuadRoutes::cosMax = std::cos((90-maxAngleDelta)/180.f*3.14159);
QuadRoutes::QuadRoutes(Vertex ne, Vertex se, Vertex sw, Vertex nw) : Chose(), ne(ne), se(se), sw(sw), nw(nw) {
addEntropy(ne, se, sw, nw);
triangulation();
}
int QuadRoutes::width() { return std::abs(this->ne.x - this->sw.x); }
int QuadRoutes::height() { return std::abs(this->ne.y - this->sw.y); }
bool QuadRoutes::subdivide() {
children.reserve(9);
// TODO : faire ces calculs sur des Vertex2d.
int slen = (se-sw).norm();
int nlen = (ne-nw).norm();
int minsnlen = std::min(slen,nlen);
// constraint: min(slen, nlen) - maxdelta*2 ≥ minchildsize
// constraint: maxdelta ≤ min(slen,nlen)/4
int xmaxdelta = std::min(minsnlen/4, (minsnlen-minchildsize)/2);
float sxpos = slen/2 + hashInRange(seed, 0, -xmaxdelta, xmaxdelta);
float nxpos = nlen/2 + hashInRange(seed, 0, -xmaxdelta, xmaxdelta);
Vertex s = (sw * sxpos / slen) + (se * (slen - sxpos) / slen);
Vertex n = (nw * nxpos / nlen) + (ne * (nlen - nxpos) / nlen);
int wlen = (nw-sw).norm();
int elen = (ne-se).norm();
int minwelen = std::min(wlen,elen);
// constraint: min(wlen, elen) - maxdelta*2 ≥ minchildsize
// constraint: maxdelta ≤ min(wlen,elen)/4
int ymaxdelta = std::min(minwelen/4, (minwelen-minchildsize)/2);
float wypos = wlen/2 + hashInRange(seed, 0, -ymaxdelta, ymaxdelta);
float eypos = elen/2 + hashInRange(seed, 0, -ymaxdelta, ymaxdelta);
Vertex w = (nw * wypos / wlen) + (sw * (wlen - wypos) / wlen);
Vertex e = (ne * eypos / elen) + (se * (elen - eypos) / elen);
Vertex split = intersection(s,n,w,e);
std::cout << "n-split-e=" << cosAngle(n,split,e) << " minmax=" << cosMax << std::endl;
// Créer 4 quad (qne, qse, qsw, qnw), puis :
Quad q[4] = {
Quad(ne, e, split, n),
Quad(se, s, split, e),
Quad(sw, w, split, s),
Quad(nw, n, split, w),
};
for (int c = NE; c <= NW; ++c) {
q[c].offset(W,-hrw); q[c].offset(S,-hrw);
}
addChild(new Carrefour(q[NE].sw, q[SE].sw, q[SW].sw, q[NW].sw));
addChild(new Route(q[NW].se, q[NE].nw, q[NE].sw, q[NW].sw));
addChild(new Route(q[NE].se, q[SE].nw, q[SE].sw, q[NE].sw));
addChild(new Route(q[SE].se, q[SW].nw, q[SW].sw, q[SE].sw));
addChild(new Route(q[SW].se, q[NW].nw, q[NW].sw, q[SW].sw));
addChild(sub(q[NE].ne, q[NE].se, q[NE].sw, q[NE].nw));
addChild(sub(q[SE].ne, q[SE].se, q[SE].sw, q[SE].nw));
addChild(sub(q[SW].ne, q[SW].se, q[SW].sw, q[SW].nw));
addChild(sub(q[NW].ne, q[NW].se, q[NW].sw, q[NW].nw));
return true;
}
void QuadRoutes::triangulation() {
triangles.reserve(2);
addTriangle(new Triangle(ne, nw, sw, 0xc0, 0xc0, 0xc0));
addTriangle(new Triangle(sw, se, ne, 0xc0, 0xc0, 0xc0));
}
Chose* QuadRoutes::sub(Vertex ne, Vertex se, Vertex sw, Vertex nw) {
if ((ne - se).norm() < minQuadSize ||
(se - sw).norm() < minQuadSize ||
(sw - nw).norm() < minQuadSize ||
(nw - ne).norm() < minQuadSize) {
return new Batiment(ne, se, sw, nw);
} else {
return new QuadRoutes(ne, se, sw, nw);
}
}
std::ostream& operator<<(std::ostream& os, const QuadRoutes* r) {
return os << *r;
}
std::ostream& operator<<(std::ostream& os, const QuadRoutes& r) {
return os << "QuadRoutes " << r.ne << "-" << r.sw;
}

View File

@ -1,33 +0,0 @@
#ifndef _RULES_QUADROUTES_HH_
#define _RULES_QUADROUTES_HH_
#include "all_includes.hh"
// QuadRoutes est un quadrilatère de routes avec des angles aux coins entre 70° et 110°, et des côtés de longueur >= 10.
class QuadRoutes : public Chose {
public:
Vertex ne;
Vertex se;
Vertex sw;
Vertex nw;
public:
static const int hrw = 250; // half road width : 2,50m.
static const int minchildsize = 1000;
static const int minQuadSize = 2500;
static const int maxAngleDelta = 20; // 90±20°
static const float cosMin;
static const float cosMax;
public:
QuadRoutes(Vertex ne, Vertex se, Vertex sw, Vertex nw);
int width();
int height();
virtual bool subdivide();
virtual void triangulation();
friend std::ostream& operator<<(std::ostream& os, const QuadRoutes& r);
friend std::ostream& operator<<(std::ostream& os, const QuadRoutes* r);
private:
Chose* sub(Vertex ne, Vertex se, Vertex sw, Vertex nw);
};
#endif

View File

@ -1,71 +0,0 @@
#include "all_includes.hh"
RectangleRoutes::RectangleRoutes(Vertex ne, Vertex sw) : Chose(), ne(ne), sw(sw) {
addEntropy(ne, sw);
triangulation();
}
int RectangleRoutes::width() { return std::abs(this->ne.x - this->sw.x); }
int RectangleRoutes::height() { return std::abs(this->ne.y - this->sw.y); }
bool RectangleRoutes::subdivide() {
children.reserve(9);
int splitXMin = this->sw.x + std::max(4, this->width()*1/4);
int splitXMax = this->ne.x - std::max(4, this->width()*1/4);
int splitYMin = this->sw.y + std::max(4, this->height()*1/4);
int splitYMax = this->ne.y - std::max(4, this->height()*1/4);
Vertex split(
hashInRange(this->seed, 0, splitXMin, splitXMax),
hashInRange(this->seed, 1, splitYMin, splitYMax),
0 // TODO
);
// TODO : addChild(…);
addChild(new Carrefour(split + Vertex(1,1,0), split + Vertex(1,-1,0), split + Vertex(-1,-1,0), split + Vertex(-1,1,0)));
// routes au NESW du carrefour
// TODO : la plupart des zéros en z sont faux…
Vertex roadEndN(split.x, this->ne.y, 0);
Vertex roadEndE(this->ne.x, split.y, 0);
Vertex roadEndS(split.x, this->sw.y, 0);
Vertex roadEndW(this->sw.x, split.y, 0);
// TODO : addChild(…);
Route* rn = new Route(roadEndN + Vertex(+1,0,0), split + Vertex(+1,+1,0), split + Vertex(-1,+1,0), roadEndN + Vertex(-1,0,0)); // N
Route* re = new Route(roadEndE + Vertex(0,+1,0), roadEndE + Vertex(0,-1,0), split + Vertex(+1,-1,0), split + Vertex(+1,+1,0)); // E
Route* rs = new Route(split + Vertex(+1,-1,0), roadEndS + Vertex(+1,0,0), roadEndS + Vertex(-1,0,0), split + Vertex(-1,-1,0)); // S
Route* rw = new Route(split + Vertex(-1,+1,0), split + Vertex(-1,-1,0), roadEndW + Vertex(0,-1,0), roadEndW + Vertex(0,+1,0)); // W
addChild(rn);
addChild(re);
addChild(rs);
addChild(rw);
// Sous-quartiers
addChild(sub(ne, re->nw)); // sous-quartier NE
addChild(sub(re->se, rs->se)); // sous-quartier SE
addChild(sub(rs->nw, sw)); // sous-quartier SW
addChild(sub(rn->nw, rw->nw)); // sous-quartier NW
return true;
}
void RectangleRoutes::triangulation() {
triangles.reserve(2);
Vertex nw(this->sw.x, this->ne.y, 0);
Vertex se(this->ne.x, this->sw.y, 0);
addTriangle(new Triangle(this->ne, nw, this->sw, 0xc0, 0xc0, 0xc0));
addTriangle(new Triangle(this->sw, se, this->ne, 0xc0, 0xc0, 0xc0));
}
Chose* RectangleRoutes::sub(Vertex ne, Vertex sw) {
Segment rect = Segment(ne,sw);
if (rect.width() < 10 || rect.height() < 10) {
return new Batiment(ne, Vertex(ne.x, sw.y, 0), sw, Vertex(sw.x, ne.y, 0));
} else {
return new RectangleRoutes(ne, sw);
}
}
std::ostream& operator<<(std::ostream& os, const RectangleRoutes* r) {
return os << *r;
}
std::ostream& operator<<(std::ostream& os, const RectangleRoutes& r) {
return os << "RectangleRoutes " << r.ne << "-" << r.sw;
}

View File

@ -1,25 +0,0 @@
#ifndef _RULES_RECTANGLEROUTES_HH_
#define _RULES_RECTANGLEROUTES_HH_
#include "all_includes.hh"
// RectangleRoutes est un quadrilatère de routes avec des angles aux coins égaux à 90°, et des côtés de longueur >= 10.
class RectangleRoutes : public Chose {
public:
Vertex ne;
Vertex sw;
public:
RectangleRoutes(Vertex ne, Vertex sw);
int width();
int height();
virtual bool subdivide();
virtual void triangulation();
private:
Chose* sub(Vertex ne, Vertex sw);
public:
friend std::ostream& operator<<(std::ostream& os, const RectangleRoutes& r);
friend std::ostream& operator<<(std::ostream& os, const RectangleRoutes* r);
};
#endif

View File

@ -13,3 +13,8 @@ int Segment::width() {
int Segment::height() {
return std::abs(u.y - v.y);
}
Vertex Segment::randomPos(int seed, int n, int a, int b) {
int pos = hashInRange(seed, n, a, b);
return (u * pos + v * (100-pos)) / 100;
}

View File

@ -12,6 +12,7 @@ public:
int length();
int width();
int height();
Vertex randomPos(int seed, int n, int a, int b); // Renvoir un vertex sur le segment [u,v], à une position entre a% and b%.
};
#endif