diff --git a/src/modules/stream/ui/building_layout_graph.h b/src/modules/stream/ui/building_layout_graph.h index 8ba7567..1ca8997 100644 --- a/src/modules/stream/ui/building_layout_graph.h +++ b/src/modules/stream/ui/building_layout_graph.h @@ -59,15 +59,14 @@ public: const String &module_name); void room_growth_module(flecs::world &ecs, const String &module_name); + void zones_graph_module(flecs::world &ecs, + const String &module_name); void create_floor_components( flecs::entity floor_e, flecs::entity base_floor_e, const WorldEditor::components::buildings_layout_grid_size &size); - void create_region(flecs::entity floor_e, flecs::entity seed_e, - flecs::entity region_e, - const Vector2i &position, float area); bool check_region(flecs::entity floor_e, int index, - const Rect2i &rect); + const Rect2i &rect) const; graph_module(flecs::world &ecs); }; }; \ No newline at end of file diff --git a/src/modules/stream/ui/graph_module.cpp b/src/modules/stream/ui/graph_module.cpp index 73a2cd2..aae752d 100644 --- a/src/modules/stream/ui/graph_module.cpp +++ b/src/modules/stream/ui/graph_module.cpp @@ -273,15 +273,13 @@ void BuildingLayoutGraph::graph_module::create_floor_components( floor_e.set( { Set(), size.grid_size, size.growth_size }); floor_e.set( - { Vector() }); + { Vector(), false }); floor_e.add(base_floor_e); } -void BuildingLayoutGraph::graph_module::create_region(flecs::entity floor_e, - flecs::entity seed_e, - flecs::entity region_e, - const Vector2i &position, - float area) +void growth_regions::create_region(flecs::entity floor_e, flecs::entity seed_e, + flecs::entity region_e, + const Vector2i &position, float area) { int i; struct growth_regions::region r; @@ -291,8 +289,9 @@ void BuildingLayoutGraph::graph_module::create_region(flecs::entity floor_e, r.rect.size = Vector2i(1, 1); r.remains_area = MAX((int)(area * 2.0f) / 16 + 1, 16); r.can_grow_square = true; + r.can_grow = true; bool ok = true; - assert(check_region(floor_e, -1, r.rect)); + assert(check_region(-1, r.rect)); const struct growth_regions *reg = floor_e.get(); for (i = 0; i < reg->regions.size(); i++) { if (reg->regions[i].region_et == r.region_et) { @@ -307,91 +306,27 @@ void BuildingLayoutGraph::graph_module::create_region(flecs::entity floor_e, r.remains_area -= 1; assert(ok); if (ok) { + floor_e.get_mut()->complete = false; floor_e.get_mut()->regions.push_back(r); floor_e.modified(); flecs::log::warn("region created for %s", region_e.path().c_str()); } } - bool BuildingLayoutGraph::graph_module::check_region(flecs::entity floor_e, int index, - const Rect2i &rect) + const Rect2i &rect) const { - const Vector ®ions = - floor_e.get()->regions; - int i; - bool ret = true; - for (i = 0; i < regions.size(); i++) { - if (i == index) - continue; - if (rect.intersects(regions[i].rect)) { - ret = false; - break; - } - } - return ret; + const growth_regions *g = floor_e.get(); + return g->check_region(index, rect); } - -BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) +void BuildingLayoutGraph::graph_module::zones_graph_module( + flecs::world &ecs, const String &module_name) { - ecs.module(); - ecs.import (); - ecs.set({}); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component() - .member("index"); - ecs.component(); - ecs.component(); - ecs.component(); - ecs.component() - .member("grid_size"); - ecs.component() - .member("area"); - ecs.component() - .member("index"); - ecs.component(); -#if 0 - ecs.component< - WorldEditor::components::buildings_layout_commands::command>() - .member("command") - .member >("args"); - ecs.component() - .member("commands"); -#endif - ecs.component(); - // get_layout_grid_base(); - // BuildingLayoutGraph::get_singleton()->get_layout_grid_base(); - const String &module_name = "::BuildingLayoutGraph::graph_module"; flecs::entity GraphSolveZones = ecs.entity("GraphSolveZones") .add(flecs::Phase) .depends_on(flecs::OnUpdate); GraphSolveZones.disable(); - flecs::entity GraphSolveUnits = ecs.entity("GraphSolveUnits") - .add(flecs::Phase) - .depends_on(GraphSolveZones); - flecs::entity GraphSolveFloors = ecs.entity("GraphSolveFloors") - .add(flecs::Phase) - .depends_on(GraphSolveUnits); - GraphSolve = ecs.entity("GraphSolve") - .add(flecs::Phase) - .depends_on(GraphSolveFloors); - flecs::entity GraphAssembleSkeleton = - ecs.entity("GraphAssembleSkeleton") - .add(flecs::Phase) - .depends_on(GraphSolve); - GraphAssembleSkeleton.disable(); - flecs::entity GraphPostSolve = ecs.entity("GraphPostSolve") - .add(flecs::Phase) - .depends_on(flecs::PostUpdate); - GraphPostSolve.disable(); ecs.system("ZonesStart") .kind(GraphSolveZones) @@ -592,7 +527,68 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) .run([module_name](flecs::iter &it) { print_line("Processing units..."); }); +} +BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) +{ + const String &module_name = "::BuildingLayoutGraph::graph_module"; + ecs.module(); + ecs.import (); + ecs.set({}); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component() + .member("index"); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component() + .member("grid_size"); + ecs.component() + .member("area"); + ecs.component() + .member("index"); + ecs.component(); +#if 0 + ecs.component< + WorldEditor::components::buildings_layout_commands::command>() + .member("command") + .member >("args"); + ecs.component() + .member("commands"); +#endif + ecs.component(); + // get_layout_grid_base(); + // BuildingLayoutGraph::get_singleton()->get_layout_grid_base(); + flecs::entity GraphPostSolve = ecs.entity("GraphPostSolve") + .add(flecs::Phase) + .depends_on(flecs::PostUpdate); + GraphPostSolve.disable(); + + zones_graph_module(ecs, module_name); + flecs::entity GraphSolveZones = + ecs.lookup((module_name + "::GraphSolveZones").ascii().ptr()); + assert(GraphSolveZones.is_valid()); + flecs::entity GraphSolveUnits = ecs.entity("GraphSolveUnits") + .add(flecs::Phase) + .depends_on(GraphSolveZones); + flecs::entity GraphSolveFloors = ecs.entity("GraphSolveFloors") + .add(flecs::Phase) + .depends_on(GraphSolveUnits); + GraphSolve = ecs.entity("GraphSolve") + .add(flecs::Phase) + .depends_on(GraphSolveFloors); + flecs::entity GraphAssembleSkeleton = + ecs.entity("GraphAssembleSkeleton") + .add(flecs::Phase) + .depends_on(GraphSolve); + GraphAssembleSkeleton.disable(); ecs.system("UnitArea") .kind(GraphSolveUnits) .without() diff --git a/src/modules/stream/ui/graph_module.h b/src/modules/stream/ui/graph_module.h index 8fe3e0e..d9eda53 100644 --- a/src/modules/stream/ui/graph_module.h +++ b/src/modules/stream/ui/graph_module.h @@ -7,8 +7,63 @@ struct growth_regions { Rect2i rect; int remains_area; bool can_grow_square; + bool can_grow; + bool can_grow_region() const + { + bool ret = can_grow; + if (remains_area <= 0) + ret = false; + return ret; + } + bool update_region_size(Rect2i &mrect) + { + bool ret = false; + int old_area = rect.get_area(); + int new_area = mrect.get_area(); + int area_diff = new_area - old_area; + if (area_diff > 0) { + rect = mrect; + remains_area -= area_diff; + ret = true; + } + if (remains_area <= 0) { + can_grow_square = false; + can_grow = false; + } + flecs::log::dbg("update_region_size %d -> %d", + area_diff, ret); + return ret; + } }; Vector regions; + bool complete; + bool check_region(int index, const Rect2i &rect) const + { + int i; + bool ret = true; + for (i = 0; i < regions.size(); i++) { + if (i == index) + continue; + if (rect.intersects(regions[i].rect)) { + ret = false; + break; + } + } + flecs::log::dbg("check_region: %d -> %d", index, ret); + return ret; + } + bool update_region_size(int index, Rect2i &mrect) + { + bool ret = false; + bool ok = check_region(index, mrect); + if (ok) + ret = regions.write[index].update_region_size(mrect); + flecs::log::dbg("update_region_size %d -> %d", index, ret); + return ret; + } + void create_region(flecs::entity floor_e, flecs::entity seed_e, + flecs::entity region_e, const Vector2i &position, + float area); }; struct make_random { diff --git a/src/modules/stream/ui/graph_module_growth.cpp b/src/modules/stream/ui/graph_module_growth.cpp index ebdf02e..dc86f44 100644 --- a/src/modules/stream/ui/graph_module_growth.cpp +++ b/src/modules/stream/ui/graph_module_growth.cpp @@ -4,6 +4,598 @@ #include "building_layout_graph.h" #include "graph_module.h" +struct grid_calc { + int grid_size; + int index2x(int index) + { + return index % grid_size; + } + int index2y(int index) + { + return index / grid_size; + } + void get_cadidates(int index, int *candidates) + { + int i, j, idx = 0; + int x = index2x(index); + int y = index2y(index); + + for (i = -1; i < 2; i++) { + for (j = -1; j < 2; j++) + if (i != 0 || j != 0) { + int cx = x + i, cy = y + j; + if (cx >= 0 && cx < grid_size && + cy >= 0 && cy < grid_size) { + int id = cx + grid_size * cy; + candidates[idx++] = id; + } + } + } + } + grid_calc(flecs::entity layout_e) + : grid_size(layout_e.get() + ->grid_size) + { + assert(layout_e.is_valid()); + } + grid_calc(int grid_size) + : grid_size(grid_size) + { + assert(grid_size > 0); + } +}; +struct grid_misc { + LocalVector > positions; + LocalVector accepted; + struct make_random r; + grid_misc() + : r(100) + { + } + void get_dim_candidates(const Vector2i &base, int dim, + Vector2i *candidates) + { + int i; + std::vector candidates_data = { + /* clang-format off */ + { base.x + dim, base.y }, + { base.x, base.y + dim }, + { base.x - dim, base.y }, + { base.x, base.y - dim }, + { base.x + dim, base.y }, + { base.x, base.y + dim }, + { base.x - dim, base.y }, + { base.x, base.y - dim }, + /* clang-format on */ + }; + for (i = 0; i < (int)candidates_data.size(); i++) + candidates[i] = candidates_data[i]; + } + int get_floor_index(flecs::entity e) const + { + return e.get() + ->index; + } + flecs::entity get_grid_floor(flecs::entity grid_e, + flecs::entity graph_floor_e) + { + assert(grid_e.is_valid()); + assert(graph_floor_e.is_valid()); + int floor_index = get_floor_index(graph_floor_e); + String floor_name = "floor_" + itos(floor_index); + flecs::log::dbg("floor: %s", floor_name.ascii().ptr()); + flecs::entity floor_e = grid_e.lookup(floor_name.ascii().ptr()); + assert(floor_e.is_valid()); + return floor_e; + } + bool check_duplicates() + { + int i, j; + bool result = true; + for (i = 0; i < (int)positions.size(); i++) + for (j = 0; j < (int)positions.size(); j++) { + if (i == j) + continue; + if (positions[i].second == + positions[j].second) { + flecs::log::err("duplicate positions"); + result = false; + goto out; + } + } +out: + return result; + } + struct region_desc { + flecs::entity region_e; + int cell_id; + Vector2i position; + float area; + template + void create_region(T *obj, flecs::entity grid_floor_e) + { + flecs::entity cell_e = obj->create_cell( + grid_floor_e, region_e, cell_id); + growth_regions *g = + grid_floor_e.get_mut(); + g->create_region(grid_floor_e, cell_e, region_e, + position, area); + flecs::log::warn("grid cell: %s", + cell_e.path().c_str()); + } + }; + Vector regions; + void setup_floor(flecs::entity grid_floor_e, int grid_size, + BuildingLayoutGraph::graph_module *obj) + { + int i; + assert(grid_floor_e.is_valid()); + flecs::entity grid_e = grid_floor_e.parent(); + assert(grid_e.is_valid()); + flecs::log::dbg("grid: %s", grid_e.path().c_str()); + flecs::log::warn("grid floor: %s", grid_floor_e.path().c_str()); + assert(check_duplicates()); + int region_count = 0; + regions.resize(positions.size()); + for (i = 0; i < (int)positions.size(); i++) { + int cell_id = positions[i].second.x + + grid_size * positions[i].second.y; + flecs::entity region_e = + grid_e.world().entity(positions[i].first); + assert(region_e.is_valid()); + float area = get_entity_area(region_e); + Rect2i check; + check.position = positions[i].second; + check.size = Vector2i(1, 1); + assert(obj->check_region(grid_floor_e, -1, check)); + regions.write[region_count++] = { region_e, cell_id, + positions[i].second, + area }; + } + regions.resize(region_count); + } + void place_regions(flecs::entity grid_floor_e, + BuildingLayoutGraph::graph_module *obj) + { + int i; + for (i = 0; i < regions.size(); i++) + regions.write[i].create_region(obj, grid_floor_e); + } + float get_entity_area(flecs::entity e) const + { + return e.get() + ->area; + } + float get_entity_area(flecs::world &&ecs, flecs::entity_t et) const + { + flecs::entity e = ecs.entity(et); + return e.get() + ->area; + } + float get_entity_area(flecs::world &ecs, flecs::entity_t et) const + { + flecs::entity e = ecs.entity(et); + return e.get() + ->area; + } + int get_base_radius(flecs::world &&ecs, flecs::entity_t et) const + { + float base_area = get_entity_area(ecs, et); + int base_radius = (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + return base_radius; + } + int get_base_radius(flecs::world &ecs, flecs::entity_t et) const + { + float base_area = get_entity_area(ecs, et); + int base_radius = (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + return base_radius; + } + int distance_squared(const Vector2i &p1, const Vector2i &p2) const + { + int lx = p2.x - p1.x; + int ly = p2.y - p1.y; + return lx * lx + ly * ly; + } + bool check_candidates_tolerance(const Vector2i check) + { + int i; + bool ret = true; + for (i = 0; i < (int)positions.size(); i++) + if (distance_squared(check, positions[i].second) < 1) { + ret = false; + break; + } + return ret; + } + bool check_candidate(const Vector2i &candidate) + { + int m; + bool bad = false; + for (m = 0; m < (int)positions.size(); m++) { + Rect2i check1, check2; + check1.position = positions[m].second; + check1.size = Vector2i(1, 1); + check2.position = candidate; + check2.size = Vector2i(1, 1); + if (check1.intersects(check2)) { + bad = true; + break; + } + } + return !bad; + } + bool accept_candidate(flecs::entity ce, const Vector2i &candidate, + float area) + { + int k; + int local_radius = (int)((Math::sqrt(area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + bool ok = false; + for (k = 0; k < (int)positions.size(); k++) { + assert(k < (int)positions.size()); + flecs::entity_t pbase_et = positions[k].first; + float parea = get_entity_area(ce.world(), pbase_et); + int pdim = (int)((Math::sqrt(parea) * 1.5f) / 2.0f) / + 4; /* radius converted to grid 4x4*/ + int radius = pdim + local_radius; + int radius_sq = radius * radius; + if (distance_squared(positions[k].second, candidate) < + radius_sq) + continue; + assert(check_candidates_tolerance(candidate)); + accepted.push_back(candidate); + ok = true; + } + return ok; + } + bool process_candidates( + flecs::entity ce, const Vector2i &base, float area, + const WorldEditor::components::buildings_layout_grid_size &size, + flecs::entity_t base_et, int dim) + { + int j; + int base_radius = get_base_radius(ce.world(), base_et); + base_radius = MAX(1, base_radius); + int local_radius = (int)((Math::sqrt(area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + // int dim = base_radius + local_radius; + // dim = MAX(4, dim); + int md = 1; + if (size.grid_size > 30) + md = 8; + else if (size.grid_size > 20) + md = 6; + else if (size.grid_size > 15) + md = 4; + else if (size.grid_size > 10) + md = 2; + Rect2i clip(md, md, size.grid_size - md * 2, + size.grid_size - md * 2); + Vector2i candidates[8]; + get_dim_candidates(base, dim, &candidates[0]); + for (j = 0; + j < (int)(sizeof(candidates) / sizeof(candidates[0])); + j++) { + print_line("base: " + itos(base.x) + ", " + + itos(base.y)); + print_line( + "possible candidate: " + itos(candidates[j].x) + + ", " + itos(candidates[j].y)); + if (!clip.has_point(candidates[j])) { + print_line("clipped by grid field"); + print_line(clip.operator String()); + continue; + } + if (!check_candidates_tolerance(candidates[j])) { + print_line("too close to existing positions"); + continue; + } + if (!check_candidate(candidates[j])) { + print_line( + "too close to existing positions (rect)"); + continue; + } + print_line("valid candidate: " + itos(candidates[j].x) + + ", " + itos(candidates[j].y)); + accept_candidate(ce, candidates[j], area); + } + if (accepted.size() > 0) + return true; + else + return false; + } + void filter_candidates( + flecs::entity ce, float area, + const WorldEditor::components::buildings_layout_grid_size &size) + { + if (positions.empty()) { + /* starting at grid center */ + Vector2i start_pos(size.grid_size / 2, + size.grid_size / 2); + positions.push_back(Pair( + ce.id(), start_pos)); + return; + } + while (1) { + int which = which_position(); + const Vector2i &base = positions[which].second; + flecs::entity_t base_et = positions[which].first; + int base_radius = get_base_radius(ce.world(), base_et); + base_radius = MAX(1, base_radius); + int local_radius = + (int)((Math::sqrt(area) * 1.6f) / 2.0f) / + 4; /* grid conversion */ + local_radius = MAX(1, local_radius); + int dim = base_radius + local_radius; + dim = MAX(1, dim); + process_candidates(ce, base, area, size, base_et, dim); + if (accepted.size() == 0) { + assert(positions.size() > 0); + continue; + } + assert(accepted.size() > 0); + const Vector2i &selected = accepted[which_selected()]; + assert(check_candidates_tolerance(selected)); + Pair m(ce.id(), selected); + check_candidate(selected); + positions.push_back(m); + flecs::log::warn("add position: %d, %d", selected.x, + selected.y); + accepted.clear(); + break; + } + } + int which_position() + { + int which; + if (positions.size() == 0) + return -1; + which = r.get() % positions.size(); + assert(which < (int)positions.size()); + return which; + } + int which_selected() + { + int which; + if (accepted.size() == 0) + return -1; + which = r.get() % accepted.size(); + assert(which < (int)accepted.size()); + return which; + } + template + void place_region_cells( + T *obj, flecs::entity grid_floor_e, + const WorldEditor::components::buildings_layout_grid_floor &fl, + growth_regions &g) + { + int i; + for (i = 0; i < g.regions.size(); i++) { + int x, y; + Rect2i rect = g.regions[i].rect; + for (x = rect.position.x; + x <= rect.position.x + rect.size.x; x++) + for (y = rect.position.y; + y <= rect.position.y + rect.size.y; y++) { + int id = x + fl.grid_size * y; + if (!fl.cells.has(id)) { + flecs::entity seed_e = + grid_floor_e.world().entity( + g.regions[i] + .seed_et); + assert(seed_e.is_valid()); + obj->queue_grow_cell(seed_e, + id); + } + } + } + } + void print_can_grow(int state, growth_regions &g, int index, + const char *what) + { + const growth_regions::region ®ion = g.regions[index]; + bool can = false; + if (!strcmp(what, "square")) + can = region.can_grow_square; + else if (!strcmp(what, "rect")) + can = region.can_grow; + + if (can) + flecs::log::dbg( + "state: %d: index %d: region can still grow %s %d", + state, index, what, region.remains_area); + else + flecs::log::dbg( + "state: %d: index %d: region can't grow %s %d", + state, index, what, region.remains_area); + if (region.can_grow_region()) + flecs::log::dbg( + "state %d: index %d: region can still continue", + state, index); + } + bool grow_state0(growth_regions &g, int index, const Rect2i &clip) + { + bool ok = true, ret = false; + Rect2i mrect; + growth_regions::region ®ion = g.regions.write[index]; + mrect = region.rect; + assert(g.check_region(index, mrect)); + + mrect = mrect.grow(1); + ok = clip.encloses(mrect); + if (!ok) + flecs::log::dbg("state: %d: index %d: out of clip area", + 0, index); + if (ok) { + ok = g.check_region(index, mrect); + if (!ok) + flecs::log::dbg( + "state: %d: index %d: check_region failed", + 0, index); + } + if (ok) { + ret = region.update_region_size(mrect); + if (!ret) + flecs::log::dbg( + "state: %d: index %d: update_region_size failed", + 0, index); + } + print_can_grow(0, g, index, "square"); + if (!ret) + flecs::log::dbg("state %d could not grow region %d: %d", + 0, index, region.remains_area); + else + flecs::log::dbg("state %d could grow region %d: %d", 0, + index, region.remains_area); + return ret; + } + bool grow_state1(growth_regions &g, int index, const Rect2i &clip) + { + int d; + bool ret = false; + Rect2i mrect; + growth_regions::region ®ion = g.regions.write[index]; + int count = 0; + for (d = 0; d < 4; d++) { + bool ok; + mrect = region.rect; + assert(g.check_region(index, mrect)); + switch (d) { + case 0: + mrect.position.y -= 1; + mrect.size.y += 1; + break; + case 1: + mrect.size.y += 1; + break; + case 2: + mrect.position.x -= 1; + mrect.size.x += 1; + break; + case 3: + mrect.size.x += 1; + break; + } + ok = clip.encloses(mrect); + if (ok) + ok = g.check_region(index, mrect); + if (ok) { + bool result = region.update_region_size(mrect); + if (result) + count++; + } + } + if (count > 0) { + ret = true; + flecs::log::dbg( + "state %d could grow region %d: %d - %d out of %d times", + 0, index, region.remains_area, count, 4); + } + print_can_grow(1, g, index, "rect"); + if (!ret) + flecs::log::dbg( + "state %d could not grow region rect %d: %d", 0, + index, region.remains_area); + return ret; + } + template + void grow_region_rects( + T *obj, flecs::entity floor_e, + WorldEditor::components::buildings_layout_grid_floor &fl, + growth_regions &g) + { + int i; + bool grown = true; + int state = 0; + if (g.complete) + return; + Rect2i clip(0, 0, fl.grid_size, fl.grid_size); + state = 0; + flecs::log::dbg("growing square"); + while (grown) { + grown = false; + int count = 0; + for (i = 0; i < g.regions.size(); i++) { + growth_regions::region ®ion = + g.regions.write[i]; + Rect2i mrect = region.rect; + flecs::log::dbg("grow_region_rects: region %d", + i); + if (!region.can_grow_region()) { + flecs::log::dbg( + "grow_region_rects: skip %d", + i); + continue; + } + mrect = region.rect; + bool result = false; + result = grow_state0(g, i, clip); + if (result) + count++; + } + if (count > 0) { + grown = true; + flecs::log::dbg("grown squares %d times of %d", + count, g.regions.size()); + } + if (!grown) + flecs::log::dbg( + "grow_region_rects: could not grow more squares"); + } + state = 1; + grown = true; + flecs::log::dbg("growing rect"); + while (grown) { + grown = false; + int count = 0; + for (i = 0; i < g.regions.size(); i++) { + growth_regions::region ®ion = + g.regions.write[i]; + Rect2i mrect = region.rect; + flecs::log::dbg("grow_region_rects: region %d", + i); + if (!region.can_grow_region()) { + flecs::log::dbg( + "grow_region_rects: skip %d", + i); + continue; + } + mrect = region.rect; + bool result = false; + result = grow_state1(g, i, clip); + if (result) + count++; + if (!result) + flecs::log::dbg( + "state %d could not grow region %d", + state, i); + else + flecs::log::dbg( + "state %d could grow region %d: %d", + state, i, region.remains_area); + } + if (count > 0) { + grown = true; + flecs::log::dbg("grown rects %d times of %d", + count, g.regions.size()); + } + if (!grown) + flecs::log::dbg( + "grow_region_rects: could not grow any more rects"); + } + flecs::log::dbg("grow_region_rects: complete"); + g.complete = true; + flecs::log::dbg("grow_region_rects: %s: done", + floor_e.path().c_str()); + } +}; + void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, const String &module_name) { @@ -12,373 +604,9 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, .add(flecs::Phase) .depends_on(flecs::OnUpdate); GraphFilter.disable(); - - struct grid_calc { - int grid_size; - int index2x(int index) - { - return index % grid_size; - } - int index2y(int index) - { - return index / grid_size; - } - void get_cadidates(int index, int *candidates) - { - int i, j, idx = 0; - int x = index2x(index); - int y = index2y(index); - - for (i = -1; i < 2; i++) { - for (j = -1; j < 2; j++) - if (i != 0 || j != 0) { - int cx = x + i, cy = y + j; - if (cx >= 0 && cx < grid_size && - cy >= 0 && cy < grid_size) { - int id = cx + - grid_size * cy; - candidates[idx++] = id; - } - } - } - } - grid_calc(flecs::entity layout_e) - : grid_size( - layout_e.get() - ->grid_size) - { - assert(layout_e.is_valid()); - } - grid_calc(int grid_size) - : grid_size(grid_size) - { - assert(grid_size > 0); - } - }; - struct grid_misc { - LocalVector > positions; - LocalVector accepted; - struct make_random r; - grid_misc() - : r(100) - { - } - void get_dim_candidates(const Vector2i &base, int dim, - Vector2i *candidates) - { - int i; - std::vector candidates_data = { - /* clang-format off */ - { base.x + dim, base.y }, - { base.x, base.y + dim }, - { base.x - dim, base.y }, - { base.x, base.y - dim }, - { base.x + dim, base.y }, - { base.x, base.y + dim }, - { base.x - dim, base.y }, - { base.x, base.y - dim }, - /* clang-format on */ - }; - for (i = 0; i < (int)candidates_data.size(); i++) - candidates[i] = candidates_data[i]; - } - void setup_floor( - flecs::entity grid_e, flecs::entity_t base_et, - const WorldEditor::components::buildings_layout_grid_size - &size, - BuildingLayoutGraph::graph_module *obj) - { - int i, j; - flecs::entity base_floor_e = - grid_e.world().entity(base_et); - flecs::log::warn("base_floor: %s", - base_floor_e.path().c_str()); - int floor_index = - base_floor_e - .get() - ->index; - String floor_name = "floor_" + itos(floor_index); - flecs::entity floor_e = - grid_e.lookup(floor_name.ascii().ptr()); - print_line("grid: " + String(grid_e.path())); - print_line("floor: " + floor_name); - assert(floor_e.is_valid()); - assert(grid_e.is_valid()); - obj->create_floor_components(floor_e, base_floor_e, - size); - flecs::log::warn("grid floor: %s", - floor_e.path().c_str()); - for (i = 0; i < (int)positions.size(); i++) - for (j = 0; j < (int)positions.size(); j++) { - if (i == j) - continue; - if (positions[i].second == - positions[j].second) - flecs::log::err( - "duplicate positions"); - assert(positions[i].second != - positions[j].second); - } - for (i = 0; i < (int)positions.size(); i++) { - int cell_id = - positions[i].second.x + - size.grid_size * positions[i].second.y; - flecs::entity region_e = grid_e.world().entity( - positions[i].first); - float area = - region_e.get() - ->area; - assert(region_e.is_valid()); - assert(floor_e.is_valid()); - Rect2i check; - check.position = positions[i].second; - check.size = Vector2i(1, 1); - assert(obj->check_region(floor_e, -1, check)); - flecs::entity cell_e = obj->create_cell( - floor_e, region_e, cell_id); - assert(cell_e.is_valid()); - obj->create_region(floor_e, cell_e, region_e, - positions[i].second, area); - flecs::log::warn("grid cell: %s", - cell_e.path().c_str()); - } - } - float get_entity_area(flecs::entity e) const - { - return e.get() - ->area; - } - float get_entity_area(flecs::world &&ecs, - flecs::entity_t et) const - { - flecs::entity e = ecs.entity(et); - return e.get() - ->area; - } - float get_entity_area(flecs::world &ecs, - flecs::entity_t et) const - { - flecs::entity e = ecs.entity(et); - return e.get() - ->area; - } - int get_base_radius(flecs::world &&ecs, - flecs::entity_t et) const - { - float base_area = get_entity_area(ecs, et); - int base_radius = - (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / - 4; /* grid conversion */ - return base_radius; - } - int get_base_radius(flecs::world &ecs, flecs::entity_t et) const - { - float base_area = get_entity_area(ecs, et); - int base_radius = - (int)((Math::sqrt(base_area) * 1.6f) / 2.0f) / - 4; /* grid conversion */ - return base_radius; - } - int distance_squared(const Vector2i &p1, - const Vector2i &p2) const - { - int lx = p2.x - p1.x; - int ly = p2.y - p1.y; - return lx * lx + ly * ly; - } - bool check_candidates_tolerance(const Vector2i check) - { - int i; - bool ret = true; - for (i = 0; i < (int)positions.size(); i++) - if (distance_squared(check, - positions[i].second) < 1) { - ret = false; - break; - } - return ret; - } - bool check_candidate(const Vector2i &candidate) - { - int m; - bool bad = false; - for (m = 0; m < (int)positions.size(); m++) { - Rect2i check1, check2; - check1.position = positions[m].second; - check1.size = Vector2i(1, 1); - check2.position = candidate; - check2.size = Vector2i(1, 1); - if (check1.intersects(check2)) { - bad = true; - break; - } - } - return !bad; - } - bool accept_candidate(flecs::entity ce, - const Vector2i &candidate, float area) - { - int k; - int local_radius = - (int)((Math::sqrt(area) * 1.6f) / 2.0f) / - 4; /* grid conversion */ - local_radius = MAX(1, local_radius); - bool ok = false; - for (k = 0; k < (int)positions.size(); k++) { - assert(k < (int)positions.size()); - flecs::entity_t pbase_et = positions[k].first; - float parea = - get_entity_area(ce.world(), pbase_et); - int pdim = (int)((Math::sqrt(parea) * 1.5f) / - 2.0f) / - 4; /* radius converted to grid 4x4*/ - int radius = pdim + local_radius; - int radius_sq = radius * radius; - if (distance_squared(positions[k].second, - candidate) < radius_sq) - continue; - assert(check_candidates_tolerance(candidate)); - accepted.push_back(candidate); - ok = true; - } - return ok; - } - bool process_candidates( - flecs::entity ce, const Vector2i &base, float area, - const WorldEditor::components::buildings_layout_grid_size - &size, - flecs::entity_t base_et, int dim) - { - int j; - int base_radius = get_base_radius(ce.world(), base_et); - base_radius = MAX(1, base_radius); - int local_radius = - (int)((Math::sqrt(area) * 1.6f) / 2.0f) / - 4; /* grid conversion */ - local_radius = MAX(1, local_radius); - // int dim = base_radius + local_radius; - // dim = MAX(4, dim); - int md = 1; - if (size.grid_size > 30) - md = 8; - else if (size.grid_size > 20) - md = 6; - else if (size.grid_size > 15) - md = 4; - else if (size.grid_size > 10) - md = 2; - Rect2i clip(md, md, size.grid_size - md * 2, - size.grid_size - md * 2); - Vector2i candidates[8]; - get_dim_candidates(base, dim, &candidates[0]); - for (j = 0; j < (int)(sizeof(candidates) / - sizeof(candidates[0])); - j++) { - print_line("base: " + itos(base.x) + ", " + - itos(base.y)); - print_line("possible candidate: " + - itos(candidates[j].x) + ", " + - itos(candidates[j].y)); - if (!clip.has_point(candidates[j])) { - print_line("clipped by grid field"); - print_line(clip.operator String()); - continue; - } - if (!check_candidates_tolerance( - candidates[j])) { - print_line( - "too close to existing positions"); - continue; - } - if (!check_candidate(candidates[j])) { - print_line( - "too close to existing positions (rect)"); - continue; - } - print_line("valid candidate: " + - itos(candidates[j].x) + ", " + - itos(candidates[j].y)); - accept_candidate(ce, candidates[j], area); - } - if (accepted.size() > 0) - return true; - else - return false; - } - void filter_candidates( - flecs::entity ce, float area, - const WorldEditor::components::buildings_layout_grid_size - &size) - { - if (positions.empty()) { - /* starting at grid center */ - Vector2i start_pos(size.grid_size / 2, - size.grid_size / 2); - positions.push_back( - Pair( - ce.id(), start_pos)); - return; - } - while (1) { - int which = which_position(); - const Vector2i &base = positions[which].second; - flecs::entity_t base_et = - positions[which].first; - int base_radius = - get_base_radius(ce.world(), base_et); - base_radius = MAX(1, base_radius); - int local_radius = - (int)((Math::sqrt(area) * 1.6f) / - 2.0f) / - 4; /* grid conversion */ - local_radius = MAX(1, local_radius); - int dim = base_radius + local_radius; - dim = MAX(1, dim); - process_candidates(ce, base, area, size, - base_et, dim); - if (accepted.size() == 0) { - assert(positions.size() > 0); - continue; - } - assert(accepted.size() > 0); - const Vector2i &selected = - accepted[which_selected()]; - assert(check_candidates_tolerance(selected)); - Pair m(ce.id(), - selected); - check_candidate(selected); - positions.push_back(m); - flecs::log::warn("add position: %d, %d", - selected.x, selected.y); - accepted.clear(); - break; - } - } - int which_position() - { - int which; - if (positions.size() == 0) - return -1; - which = r.get() % positions.size(); - assert(which < (int)positions.size()); - return which; - } - int which_selected() - { - int which; - if (accepted.size() == 0) - return -1; - which = r.get() % accepted.size(); - assert(which < (int)accepted.size()); - return which; - } - }; + flecs::entity GraphGridPrepare = ecs.entity("GraphGridPrepare") + .add(flecs::Phase) + .depends_on(GraphSolve); ecs.system( "CreateGrid") @@ -430,8 +658,16 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, grid.filter_candidates(ce, area.area, size); }); - grid.setup_floor(grid_e, me->get().second, size, + flecs::entity base_floor_e = + grid_e.world().entity(me->get().second); + flecs::entity grid_floor_e = + grid.get_grid_floor(grid_e, + base_floor_e); + create_floor_components(grid_floor_e, + base_floor_e, size); + grid.setup_floor(grid_floor_e, size.grid_size, this); + grid.place_regions(grid_floor_e, this); me = me->next(); } @@ -455,6 +691,7 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, "update_layout_view", varray()); // assert(false); }); +#if 0 ecs.system("GrowFloorRectRegions") @@ -463,175 +700,12 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, WorldEditor::components::buildings_layout_grid_floor &fl, growth_regions &g) { - int i; - bool grown = true; - int state = 0; - while (1) { - while (grown) { - grown = false; - for (i = 0; i < g.regions.size(); i++) { - Rect2i mrect = - g.regions[i].rect; - if (state == 0) { - if (!g.regions[i] - .can_grow_square) - continue; - if (g.regions[i] - .remains_area <= - 0) { - g.regions - .write[i] - .can_grow_square = - false; - continue; - } - } - mrect = g.regions[i].rect; - int old_area = mrect.get_area(); - if (state == 0) { - mrect = g.regions[i] - .rect; - - assert(check_region( - floor_e, i, - mrect)); - mrect = mrect.grow(1); - Rect2i clip( - 0, 0, - fl.grid_size, - fl.grid_size); - if (!clip.encloses( - mrect)) - continue; - bool ok = check_region( - floor_e, i, - mrect); - if (!ok) - continue; - int new_area = - mrect.get_area(); - int area_diff = - new_area - - old_area; - if (area_diff > 0) { - g.regions - .write[i] - .rect = - mrect; - g.regions - .write[i] - .remains_area -= - area_diff; - grown = true; - } - } else if (state == 1) { - int d; - mrect = g.regions[i] - .rect; - if (g.regions[i] - .remains_area <= - 0) - break; - bool ok = true; - for (d = 0; d < 4; - d++) { - mrect = g.regions[i] - .rect; - assert(check_region( - floor_e, - i, - mrect)); - switch (d) { - case 0: - mrect.position - .y -= - 1; - mrect.size - .y += - 1; - break; - case 1: - mrect.size - .y += - 1; - break; - case 2: - mrect.position - .x -= - 1; - mrect.size - .x += - 1; - break; - case 3: - mrect.size - .x += - 1; - break; - } - Rect2i clip( - 0, 0, - fl.grid_size, - fl.grid_size); - if (!clip.encloses( - mrect)) { - ok = false; - continue; - } - - ok = check_region( - floor_e, - i, - mrect); - if (ok) { - int new_area = - mrect.get_area(); - int area_diff = - new_area - - old_area; - if (area_diff > - 0) { - g.regions - .write[i] - .rect = - mrect; - g.regions - .write[i] - .remains_area -= - area_diff; - grown = true; - } - } - } - } - } - } - state++; - grown = true; - if (state == 2) - break; - } - for (i = 0; i < g.regions.size(); i++) { - int x, y; - Rect2i rect = g.regions[i].rect; - for (x = rect.position.x; - x <= rect.position.x + rect.size.x; x++) - for (y = rect.position.y; - y <= rect.position.y + rect.size.y; - y++) { - int id = x + fl.grid_size * y; - if (!fl.cells.has(id)) { - flecs::entity seed_e = - floor_e.world().entity( - g.regions[i] - .seed_et); - assert(seed_e.is_valid()); - queue_grow_cell(seed_e, - id); - } - } - } + struct grid_misc grid; + grid.grow_region_rects(this, floor_e, fl, g); + grid.place_region_cells(this, floor_e, fl, g); }); +#endif +#if 0 ecs.system( "GrowFloorRegions") .kind(0) @@ -677,6 +751,8 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, " index: " + itos(index)); }); }); +#endif +#if 0 ecs.system( "GrowRegions") .kind(0) @@ -711,11 +787,6 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, if (have_cell(floor_e, candidates[i])) continue; queue_grow_cell(e, candidates[i]); -#if 0 - String c_name = "cell_" + itos(candidates[i]); - flecs::entity c_e = - e.parent().lookup(c_name.ascii().ptr()); -#endif extended++; } if (extended == 0) @@ -724,6 +795,7 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, " index: " + itos(index)); e.world().defer_resume(); }); +#endif ecs.system( "GrowCommitQueue") @@ -751,40 +823,55 @@ void BuildingLayoutGraph::graph_module::growth_module(flecs::world &ecs, }); ecs.system("RunGrow") .kind(GraphFilter) - .run([module_name](flecs::iter &it) { + .run([module_name, this](flecs::iter &it) { int i; it.world().defer_suspend(); - print_line("Running grow..."); - for (i = 0; i < 100; i++) { - it.world() - .system(it.world().lookup( - (module_name + - "::GrowFloorRectRegions") - .ascii() - .ptr())) - .run(); - it.world() - .system(it.world().lookup( - (module_name + - "::GrowCommitQueue") - .ascii() - .ptr())) - .run(); - } + flecs::log::dbg("Running grow..."); + flecs::query + q = it.world() + .query_builder< + WorldEditor::components:: + buildings_layout_grid_floor, + growth_regions>() + .build(); + for (i = 0; i < 100; i++) + q.each([this](flecs::entity grid_floor_e, + WorldEditor::components:: + buildings_layout_grid_floor + &fl, + growth_regions &g) { + struct grid_misc grid; + if (g.complete) + return; + grid.grow_region_rects( + this, grid_floor_e, fl, g); + grid.place_region_cells( + this, grid_floor_e, fl, g); + }); #if 0 - it.world() - .system(it.world().lookup( - (module_name + "::GrowFloorRegions") - .ascii() - .ptr())) - .run(); - it.world() - .system(it.world().lookup( - (module_name + "::GrowCommitQueue") - .ascii() - .ptr())) - .run(); + q.each([this](flecs::entity grid_floor_e, + WorldEditor::components:: + buildings_layout_grid_floor &fl, + growth_regions &g) { + g.complete = true; + }); #endif +#if 0 + it.world() + .system(it.world().lookup( + (module_name + "::GrowFloorRectRegions") + .ascii() + .ptr())) + .run(); +#endif + it.world() + .system(it.world().lookup( + (module_name + "::GrowCommitQueue") + .ascii() + .ptr())) + .run(); it.world().defer_resume(); }); } diff --git a/src/modules/stream/ui/graph_module_rooms.cpp b/src/modules/stream/ui/graph_module_rooms.cpp index b2d275e..76ca242 100644 --- a/src/modules/stream/ui/graph_module_rooms.cpp +++ b/src/modules/stream/ui/graph_module_rooms.cpp @@ -6,8 +6,13 @@ void BuildingLayoutGraph::graph_module::room_growth_module( flecs::world &ecs, const String &module_name) { - flecs::entity GraphFilter = ecs.entity("GraphFilter"); + flecs::entity GraphFilter = + ecs.lookup((module_name + "::GraphFilter").ascii().ptr()); assert(GraphFilter.is_valid()); + flecs::entity GraphGrowUnitAreas = ecs.entity("GraphGrowUnitAreas") + .add(flecs::Phase) + .depends_on(flecs::OnUpdate); + GraphGrowUnitAreas.disable(); flecs::entity GraphMarkData = ecs.entity("GraphMarkData") .add(flecs::Phase) .depends_on(flecs::OnUpdate); @@ -16,8 +21,55 @@ void BuildingLayoutGraph::graph_module::room_growth_module( .add(flecs::Phase) .depends_on(flecs::OnUpdate); GraphProcessRooms.disable(); + ecs.system( + "AllocateUnitGrow") + .kind(GraphGrowUnitAreas) + .each([](flecs::entity ec, + const WorldEditor::components::buildings_layout_unit + &unit) { + ec.world() + .query_builder() + .build() + .each([](flecs::entity e, + const WorldEditor::components:: + buildings_layout_zone &zone) { + // assert(false); + }); + }); ecs.system("CheckGrow").kind(GraphFilter).run([module_name](flecs::iter &it) { + int failed_zones = 0, total_zones = 0; + it.world() + .query_builder() + .build() + .each([&failed_zones, &total_zones]( + flecs::entity ec, + const WorldEditor::components:: + buildings_layout_zone &zone) { + int count = 0; + ec.world() + .query_builder< + const WorldEditor::components:: + buildings_layout_grid_cell>() + .with( + ec) + .build() + .each([&count]( + flecs::entity e, + const WorldEditor::components:: + buildings_layout_grid_cell + &cell) { + count++; + }); + if (count == 0) + failed_zones++; + total_zones++; + }); + if (failed_zones > 0) + flecs::log::err("failed to allocate zones: %d/%d", + failed_zones, total_zones); flecs::query q = it.world() @@ -35,7 +87,7 @@ void BuildingLayoutGraph::graph_module::room_growth_module( count_left += MAX(0, fl.size_left); }); // assert(false); - if (count_run > 0 && count_left <= 0) { + if (count_run > 0 && count_left <= 0 && failed_zones == 0) { it.world() .lookup((module_name + "::GraphFilter") .ascii() @@ -48,6 +100,20 @@ void BuildingLayoutGraph::graph_module::room_growth_module( .ptr()) .enable(); print_line("Mark started..."); + } else if (count_run > 0 && count_left <= 0 && + failed_zones > 0) { + it.world() + .lookup((module_name + "::GraphFilter") + .ascii() + .ptr()) + .disable(); + print_line("Grow done"); + it.world() + .lookup((module_name + "::GraphGrowUnitAreas") + .ascii() + .ptr()) + .enable(); + print_line("Grow unit started..."); } }); ecs.system( @@ -377,4 +443,4 @@ void BuildingLayoutGraph::graph_module::room_growth_module( "not all rooms were assigned: %d < %d", assigned_count, count); }); -} \ No newline at end of file +}