BuildingSession applies the command to the building model and builds the geometry again.
This gives you three things for free: a model that is always valid, undo and redo, and a 2D view and a 3D view that always agree.
What a session does with a command
1
Check
The session checks the command and the model that results from it.
2
Build
It builds the new geometry apart from your scene. Walls join again and openings cut again.
3
Publish
If the build is good, the session puts the new geometry below
session.root and adds the edit
to the history.Send a command
The commands
See each field in the
BuildingEditCommand type.
Add, replace and remove
add-element, replace-element and remove-element name a list of the storey: slabs, roofs, spaces, stairs, columns, beams, railings or instances. An add gives the new element. A replace gives the elementId and the full new element. A remove gives only the elementId.
assetId, and add-asset and replace-asset have the asset. add-storey has the full storey. remove-storey has its storeyId. replace-storey-settings has the storeyId and the settings: the name, the elevation, the height and the usage together. replace-mass has the mass, or no mass to remove it, and the settings of each storey of the building in the new order. A storey id that stays keeps its elements. add-building has the full building; remove-building has its buildingId. replace-site has the full site, or no site to remove it.
The session rejects these commands in these conditions:
- An
addwith an id that exists: an element in its list, an asset in the model, a storey in its building. - A
replaceor aremovewith an id that does not exist. - A
replace-elementwhose new element has a different id. - A
remove-assetof an asset that an instance uses. - A
remove-storeyof the last storey of a building. - A
replace-massthat removes a storey that holds an element. - An
add-buildingwith an id that any object of the model has, and aremove-buildingof the last building. - A
replace-sitewith nositewhen the model has none.
ProjectManager does, and it also rejects a remove-storey or a replace-mass that removes a storey that a plan view names (storey-in-use), a remove-building of a building that a saved view shows (building-in-use), and a replace-site that removes a site that a view lists (site-in-use).
Doors and windows in walls
A door, a window or a hole fits on its wall when its whole cut lies between the joints of that wall. Then the wall has a hole for it. Floorkit uses this one fit rule in the build, in the commands and in the drags.add-hosted-opening puts a door or a window at an offset along a wall. move-hosted-opening and rehost-hosted-opening move a door, a window or a hole to an offset. The opening must fit at that offset, or the session rejects the command. The issue opening-does-not-fit of the error gives the offsets where the opening fits and the nearest one. add-hosted-opening and rehost-free-opening also reject an opening over the seam of a T stem; the offsets in the issue then keep clear of the seams. These commands need the geometry kernel: applyBuildingEdit rejects them with resolved-edit-required. It also rejects a move-wall-point of any wall in a storey with hosted openings, because the move can make a door of another wall stop fitting.
A saved model can have an opening that does not fit at its offset. Opening the model does not change it. The build draws the opening at its saved offset without a hole, and gives the warning opening-does-not-fit in session.currentBuild.warnings, with the same numbers. To repair it, move the opening to an offset where it fits.
An edit does not change an opening that it does not carry. The session rejects an edit that makes an opening that fits stop fitting, or that puts the seam of a T stem under it. An opening that does not fit never blocks an edit. An edit of the wall of an opening carries the opening at the same distance from the start of its segment.
A drag on a door in a drawing becomes a move-hosted-opening with resolveViewEditCandidate. Give it the current build. The drag starts at the saved offset, and the door stops at the nearest place where it fits, as in the live editor. A drag that cannot move the door gives the error invalid-drag.
A batch of commands
One change of the user can need several commands, such as a paste of several elements. Put them in onebatch. The session applies the commands in order to one model, builds one time and adds one step to the history. One undo takes all of them back.
- A batch is all or nothing. If one command fails,
applythrows the error of that command and the session changes nothing. The message starts with the place of the command, such ascommand 3 of 6:. - If one command has a wrong field or an unknown
kind, the code isinvalid-command, and the message starts with the place of the command too. - Each command works on the result of the commands before it. Thus a batch can add a storey and then add an element to that storey.
- The model must be valid after each command, not only after the last one. For example, in a building with a mass, two commands change the heights of two storeys, and the storeys fit the mass only after the second command. The session rejects this batch at the first command.
- The rules of each command stay the same in a batch. For example, a
remove-assetof an asset that an instance uses fails. - A batch must have one command or more, and it cannot hold a batch. In TypeScript, a batch in a batch does not compile. A list with the type
BuildingEditCommand[]does not go into a batch, because that type permits a batch. Give the list the type of its commands, such asElementListCommand[], or the typeBatchCommand['commands']. session.lastEditCommandgives the batch.
The revision number
A session has arevision. It goes up by one after each edit, undo or redo.
apply takes the revision that the edit started from. If a different edit came first, the number does not agree and apply throws stale-revision. This stops an old drag from overwriting a newer change.
Read the revision when the user starts the action. Do not read it again at the end.
Undo and redo
Keep an edit fast
A host app sends many edits. These rules keep each edit short.- Send one command through
applyfor one change of the user. If the change needs several commands, send onebatch. After a command for one slab, roof, room, stair, column, beam, railing, block instance, loose door, loose window or basic shape, Floorkit builds again only that element. The other elements of the storey keep their geometry. After a command for a wall, or for a door or window in a wall, Floorkit joins the walls of the storey again and builds again only the walls, doors and windows that changed. A command that adds a storey or changes its settings builds that storey. A command that changes an asset, or the settings of a storey in a building with a mass, builds the whole building again. A batch builds again what its commands build, one time. - Do not call
session.snapshot(),manager.snapshot(),manager.save(),saveProject,new BuildingSession(model),drawProjectViewor an export at each edit or at each frame of a drag. Each of them copies, writes or builds the whole model. Save on an action of the user or on a timer. - Do not open a new session for a change that a command covers, as
replaceBuildingin Start a building does. A new session builds the whole building and has an empty undo history. Open one to load or replace a whole building. - Read the new revision from what
applyreturns, and read the built element fromsession.currentBuild.elementsById. Do not keep an element object across a change: read the element again by its id after each change. A changed slab, roof, stair, column, beam, railing, space, basic shape, block or loose door or window is a new object, and Floorkit disposes the old object. A changed wall, and a door, a window or a hole on a wall, can stay the same object with new geometry, or can be a new object. Do not callsnapshot()to see the result.snapshot()copies the whole model. - After a change, refresh only what the event names. The event of
session.subscribeand ofmanager.subscribehaschanged: the ids of the built elements that the step built again, added or removed. An element that the list does not name keeps its object. Read each id fromsession.currentBuild.elementsById, or fromsession.currentBuild.modelObjectsByIdfor a basic shape. An id that is in neither map was removed. Ifchangedis absent, the whole building was built again: refresh all. - When a step fails, the model and the revision stay as they were. If the failed step already changed built elements, Floorkit builds them again from the saved model and sends one event with the kind
restore(the causerestorefrom the manager) and withchanged. Refresh these elements as after a step.
Doors and windows follow their wall
When a wall moves, its doors and windows move with it and the wall is cut again. If the new wall is too short for an opening, Floorkit rejects the edit. It does not remove the opening without telling you.Interactive editors
You do not have to write commands by hand for pointer actions. Floorkit has two editors that make the commands for you. Give the same session to both, and the plan and the 3D view show the same building.Plan editor
Edit walls and openings in a 2D plan.
3D editor
Edit the same building in your Three.js view.