commit 2db9bb222ae7e3981f40bd674bb55b09cfdcd827
parent 9d8a85afd69a7ef92c895efd67bfb451fbdf639e
Author: Georges Dupéron <jahvascriptmaniac+github@free.fr>
Date: Sat, 10 Dec 2011 20:55:23 +0100
Génération simplifiée, avec détection des motifs produits.
Diffstat:
18 files changed, 158 insertions(+), 289 deletions(-)
diff --git a/Makefile b/Makefile
@@ -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
diff --git a/all_includes.hh b/all_includes.hh
@@ -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
diff --git a/directions.hh b/directions.hh
@@ -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
diff --git a/main.cpp b/main.cpp
@@ -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);
diff --git a/quad.cpp b/quad.cpp
@@ -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]);
+}
diff --git a/quad.hh b/quad.hh
@@ -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
diff --git a/rules/quad.cpp b/rules/quad.cpp
@@ -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));
-}
diff --git a/rules/quad.hh b/rules/quad.hh
@@ -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
diff --git a/rules/quadcroix.cpp b/rules/quadcroix.cpp
@@ -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;
+}
diff --git a/rules/quadcroix.hh b/rules/quadcroix.hh
@@ -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
diff --git a/rules/quadrilatere.cpp b/rules/quadrilatere.cpp
@@ -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));
+}
+
diff --git a/rules/quadrilatere.hh b/rules/quadrilatere.hh
@@ -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
diff --git a/rules/quadroutes.cpp b/rules/quadroutes.cpp
@@ -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;
-}
diff --git a/rules/quadroutes.hh b/rules/quadroutes.hh
@@ -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
diff --git a/rules/rectangleroutes.cpp b/rules/rectangleroutes.cpp
@@ -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;
-}
diff --git a/rules/rectangleroutes.hh b/rules/rectangleroutes.hh
@@ -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
diff --git a/segment.cpp b/segment.cpp
@@ -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;
+}
diff --git a/segment.hh b/segment.hh
@@ -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