ProcGenKitchen
November 7, 2023 ยท View on GitHub
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
Procedurally generate a kitchen in a new scene or an existing scene.
Procedural generation algorithm
This is an explanation of how proc_gen_kitchen.create() works.
1. Set the random number generator
It is possible to explicitly set the random number generator via the rng parameter. Doing so will allow you to recreate the exact same scene later.
2. Set self.room
If the scene parameter is of type Room this is straightforward. Otherwise, select a scene from the scene parameter (which will be loaded into TDW) and get the Room from the room_index parameter.
3. Select a set of cabinetry
Cabinetry always visually match each other. A Cabinetry data object is selected randomly.
4. Create a work triangle
A work triangle defines the overall design of the kitchen space.
A work triangle is comprised of 1 or more lateral arrangements of Arrangement data objects along a wall, starting at a given distance from a corner.
In all lateral arrangements in ProcGenKitchen, the following rules are always followed:
- In the wall has windows, tall
Arrangementswill be replaced with shorterArrangements; seeProcGenKitchen.TALL_ARRANGEMENTS. For work triangles, tallArrangementswill be replaced withKitchenCounter. - Non-continuous walls (e.g. a wall with a doorway) will never have a lateral arrangement.
- Each wall has two corresponding corners. For example, a
northwall has cornersnorth_westandnorth_east. One of these corners is selected for the start of the lateral arrangement. Unless otherwise noted, the corner is chosen randomly. - A lateral arrangement has a list of
Arrangements. They may not all fit along the wall, in which case as manyArrangementsare added as possible.
Work triangle lateral arrangements have the following additional shared rules:
- Each work triangle lateral arrangement has secondary arrangements appended to its list to spatially lengthen it. Possibilities are: [
Basket,Painting,Void,Radiator,Stool,Suitcase]. The selection is random with weighted probability; seeProcGenKitchen.SECONDARY_ARRANGEMENTS["append"].
In TDW there are four possible work triangles:
4a. Straight
A single lateral arrangement along one of the longer walls.
Requirements: At least one continuous longer wall.
- If one of the longer walls has windows, it will be used for the lateral arrangement. Otherwise, the wall is chosen randomly.
- There are two possible lateral arrangements, chosen randomly:
- [
Refrigerator,Dishwasher,Sink,KitchenCounter,Stove,KitchenCounter,Shelf] - [
Shelf,KitchenCounter,Stove,KitchenCounter,Sink,Dishwasher,Refrigerator]
4b. Parallel
Two lateral arrangements along both longer walls.
Requirements: Two continuous longer walls.
- If one of the longer walls has windows, it will be used for the lateral arrangement with the sink. Otherwise, each lateral arrangement uses a random longer wall.
- For the first lateral arrangement, there are two possibilities, chosen randomly:
- [
Refrigerator,Dishwasher,Sink,KitchenCounter,KitchenCounter] - [
KitchenCounter,KitchenCounter,Sink,Dishwasher,Refrigerator]
- For the second lateral arrangement, there are two possibilities, chosen randomly:
- [
KitchenCounter,Stove,KitchenCounter,KitchenCounter,KitchenCounter] - [
KitchenCounter,KitchenCounter,KitchenCounter,Stove,KitchenCounter]
4c. L-Shape
One lateral arrangement along a longer wall and one lateral arrangement along a shorter wall. The two arrangements share a common corner.
Requirements: At least one continuous longer wall and one continuous shorter wall.
- If one of the longer walls has windows, it will be used for the longer lateral arrangement. Otherwise, the wall is chosen randomly.
- There are two possible longer lateral arrangements, each with a
KitchenCounterTopat the end. If both shorter walls are continuous, the selected lateral arrangement is random. Otherwise,ProcGenKitchenfinds a corner shared by the longer wall and a continuous corner wall and selects the arrangement in which theKitchenCounterTopis placed at the common corner; for example if the longer wall isnorthand the only continuous wall iswestthen the longer arrangement is the first of the following two options because itKitchenCounterTopwill be placed at the northwest corner.
- [
KitchenCounterTop,Sink,Dishwasher,Stove,KitchenCounter,Shelf] - [
Shelf,KitchenCounter,Stove,Dishwasher,Sink,KitchenCounterTop]
- The shorter lateral arrangement is placed at the corresponding valid wall (see above). There are two possibilities, chosen randomly:
- [
Void,KitchenCounter,KitchenCounter,Refrigerator,Shelf] - [
Void,KitchenCounter,Refrigerator,KitchenCounter,Shelf]
4d. U-Shape
One lateral arrangement along a longer wall and two lateral arrangements along both shorter walls.
Requirements: At least one continuous longer wall and two continuous shorter walls.
- If one of the longer walls has windows, it will be used for the longer lateral arrangement. Otherwise, the wall is chosen randomly.
- There are two possible longer lateral arrangements, chosen randomly:
- [
KitchenCounterTop,Sink,KitchenCounter,Stove,KitchenCounter,KitchenCounter] - [
KitchenCounterTop,KitchenCounter,Stove,KitchenCounter,Sink,KitchenCounter]
- Each of the longer lateral arrangements will have additional 20
KitchenCountersappended. It is unlikely that there will ever actually be this many counters. Just before running out of space along the wall, the lateral arrangement will add aKitchenCounterTopinstead (to anchor the corner). - There are two possible shorter lateral arrangements. The wall on which they appear is chosen randomly:
- [
Void,KitchenCounter,Refrigerator,KitchenCounter,Shelf] - [
Void,KitchenCounter,Dishwasher,KitchenCounter,KitchenCounter]
5. Add a table
Add a KitchenTable.
The work triangles generate a list of commands and a list of used walls. This list of used walls is used as a parameter in KitchenTable to prevent chairs from intersecting with work triangle objects.
6. Add secondary arrangements
Add secondary lateral arrangements to unused walls in the main region of the room and the walls of the alcove regions. See: ProcGenKitchen.SECONDARY_ARRANGEMENTS["main"] and ProcGenKitchen.SECONDARY_ARRANGEMENTS["alcove"].
7. Step 50 physics frames
This allows objects to stop moving.
Class Variables
| Variable | Type | Description | Value |
|---|---|---|---|
SCENE_NAMES | List[str] | The names of the default scenes. If the scene parameter in self.create() isn't set, or is set to None, a random scene from this list will be selected. | ['mm_craftroom_2a', 'mm_craftroom_2b', 'mm_craftroom_3a', 'mm_craftroom_3b', 'mm_kitchen_2a', 'mm_kitchen_2b', 'mm_kitchen_3a', 'mm_kitchen_3b'] |
SECONDARY_ARRANGEMENTS | Dict[str, Dict[str, int]] | A dictionary of "secondary arrangements". Keys: "append" (can be appended to a work triangle lateral arrangement), "main" (can be added to a lateral arrangement along an unused wall in the main region of self.room), and "alcove" (can be added to a lateral arrangement along an unused wall of an alcove region). Value: A dictionary of probabilities and names of arrangements; a higher value means that it is more likely for this arrangement to be randomly selected. | loads(Path(resource_filename(__name__, "proc_gen_kitchen_data/secondary_categories.json")).read_text()) |
TALL_ARRANGEMENTS | List[str] | Arrangements that are tall and might obscure windows. | ["refrigerator", "shelf"] |
Fields
-
rngThe random number generator. -
scene_recordTheSceneRecordfor this scene. This gets set byself.create()and can be None if thesceneparameter is aRoom. -
roomThe kitchenRoom. This gets set byself.create(). -
cabinetryTheCabinetry. This is set randomly byself.create(). -
commandsThese commands will be appended to the commands of the nextcommunicate()call. -
initializedIf True, this module has been initialized.
Functions
__init__
ProcGenKitchen()
(no parameters)
get_initialization_commands
self.get_initialization_commands()
This function gets called exactly once per add-on. To re-initialize, set self.initialized = False.
Returns: A list of commands that will initialize this add-on.
on_send
self.on_send(resp)
This is called within Controller.communicate(commands) after commands are sent to the build and a response is received.
Use this function to send commands to the build on the next Controller.communicate(commands) call, given the resp response.
Any commands in the self.commands list will be sent on the next Controller.communicate(commands) call.
| Parameter | Type | Default | Description |
|---|---|---|---|
| resp | List[bytes] | The response from the build. |
before_send
self.before_send(commands)
This is called within Controller.communicate(commands) before sending commands to the build. By default, this function doesn't do anything.
| Parameter | Type | Default | Description |
|---|---|---|---|
| commands | List[dict] | The commands that are about to be sent to the build. |
get_early_initialization_commands
self.get_early_initialization_commands()
This function gets called exactly once per add-on. To re-initialize, set self.initialized = False.
These commands are added to the list being sent on communicate() before any other commands, including those added by the user and by other add-ons.
Usually, you shouldn't override this function. It is useful for a small number of add-ons, such as loading screens, which should initialize before anything else.
Returns: A list of commands that will initialize this add-on.
create
self.create(scene)
self.create(scene, room_index=0, cabinetry_type=None, rng=None)
Procedurally generate a kitchen. The kitchen will be created on the next controller.communicate() call.
| Parameter | Type | Default | Description |
|---|---|---|---|
| scene | Union[str, SceneRecord, Room, List[Union[str, SceneRecord] | Can be a string (the name of a scene), a SceneRecord, a list of scene names and/or SceneRecord (one will chosen randomly), a Room, or None. If this is a Room, then ProcGenKitchen will assume that the scene has already been loaded, and self.scene_record will be set to None. If scene=None, a random scene from ProcGenKitchen.SCENE_NAMES will be selected. | |
| room_index | int | 0 | The index of the room in self.scene_record.rooms (assuming self.scene_record is not None; see above). |
| cabinetry_type | CabinetryType | None | A CabinetryType value that sets which kitchen cabinets, wall cabinets, and sinks to add to the scene. If None, a CabinetryType is chosen randomly. |
| rng | Union[int, np.random.RandomState] | None | The random number generator. Can be int (a random seed), np.random.RandomState, or None (a new random seed will be selected randomly). |