Onderzoek + voorstel, 2026-07-16. Ter bespreking — beslispunten onderaan.
Geverifieerd in de v0.6.0-code (src/core/studio.ts, src/App.tsx, src/core/types.ts):
| Bewerkingsfunctie | Stand v0.6 | Referentie (Revit "Modify") |
|---|---|---|
| Selecteren | ✅ één element (klik) | klik + Ctrl-klik + venster + filter |
| Verplaatsen | ✅ verslepen (drag) | + Move met 2 punten en maatinvoer |
| Kopiëren | Copy met 2 punten, meerdere plakken | |
| Knippen/plakken | ❌ | Ctrl+X/C/V, ook Paste Aligned |
| Roteren | Rotate om vrij punt met hoek | |
| Spiegelen | ❌ | Mirror over as of 2 punten |
| Uitlijnen | ❌ | Align (as-op-as), Distribute |
| Trim/Extend | ❌ | wandeinden naar snijpunt |
| Hoekverbinding (join) | ❌ (PLAN Fase 1.4, nooit gebouwd) | wall joins: L / T / verstek, automatisch |
| Splitsen | ❌ | Split Element (op punt) |
| Reeks (array) | ❌ | Array lineair/radiaal |
| Offset | ❌ | Offset op afstand |
| Eigenschappen overnemen | ❌ | Match Type Properties |
| Gaten/sparingen | meerdere openingen, vorm vrij, ook vloer/dak/shaft | |
| Typen aanmaken/bewerken | Edit Type → Duplicate; type browser | |
| Element-snapping | endpoints, midpoints, edges, loodrecht, verlengden |
Categorieën: 24 losse category-strings ("Buitenwanden", "Roosters", "MEP — Ventilatie", "Gevelafwerking", …) zonder hiërarchie. De template-kiezer is één platte dropdown van 52+ items; het lagenpaneel toont alle 24 strings als aparte lagen. Er is geen begrip "hoofdcategorie Wanden" waar buitenwand + binnenwand + Storax-lamelwand onder vallen.
Conclusie: de studio kan plaatsen en parametrisch wijzigen, maar niet bewerken in de zin die een modelleur verwacht. Dit is de grootste kloof met een bruikbaar gereedschap en hoort vóór verdere feature-uitbreiding.
Revit's Modify-tab is al ~20 jaar stabiel en is de de-facto verwachting van elke BIM-modelleur: Align · Offset · Mirror · Move · Copy · Rotate · Trim/Extend (3 varianten) · Split (2 varianten) · Array · Scale · Pin · Delete, plus Match Type en Paste Aligned. Twee lessen:
- Bewerken werkt op de selectie, niet op een modus. Eerst selecteren, dan gereedschap — of andersom; beide volgordes werken. Multi-select (Ctrl/venster) is de basis waar alles op rust.
- Wandverbindingen zijn automatisch. Twee wanden waarvan eindpunten elkaar naderen joinen vanzelf (L), een eindpunt op een wandlijf wordt een T. De join is een relatie die meebeweegt bij verslepen — geen eenmalige geometrie-operatie. Disallow Join is de uitzondering, niet de regel.
Revit-categorieën zijn hard (Walls, Floors, Roofs, …) en sturen alles: ribbon-knoppen (Architecture-tab: Wall/Door/Window/Floor/Roof), view-filters, schedules, tag-familes. Binnen/buiten is een functie-parameter op het wandtype, geen aparte categorie.
Tekla voegt toe: numeriek bewerken (elke maat direct intypen), handles op elementeinden (eindpunt verslepen i.p.v. hele element), en Copy/Move special (met rotatie/spiegeling in één stap). Les: eind-handles zijn voor lijnvormige elementen (wanden, liggers) het natuurlijkste bewerkmiddel en maken trim/extend half overbodig.
In houtbouwpakketten is een sparing een object mét betekenis (raam-, deur-, leidingsparing) dat het regelwerk lokaal wijzigt. Les voor ons: Opening als object met eigen id, type en host — niet een veldje op het element. Dat sluit ook aan op onze bestaande IfcOpeningElement-export en elementeer-logica (die snijdt al panelen op sparingen).
IFC kent IfcRelConnectsPathElements (wand-op-wand, ATSTART/ATEND/ATPATH) en IfcRelConnectsElements. Een join die wij als relatie modelleren exporteert dus 1-op-1 naar IFC — een join die alleen geometrisch is (eindpunten verschoven) niet. Les: joins als relaties opslaan, geometrie afleiden.
// Studio
selectedIds: Set<string> // vervangt selectedId: string | null
clipboard: SerializedElement[] // via serialize; ook naar localStorage (cross-project)
// PlacedElement
openings: Opening[] // vervangt opening?: Opening | null (migratie: 1→[1])
typeId?: string // verwijzing naar TypeDefinition (§4)
// Opening — uitgebreid
interface Opening {
id: string;
shape: "rect" | "round";
xPos: number; // langs de as (of x in vlak-coördinaten bij vloer/dak)
yPos?: number; // voor vlak-elementen (vloer/dak): 2e coördinaat
breedte: number; // bij round: diameter
hoogte: number;
zBottom?: number; // onderkant sparing (nu impliciet 0)
kind?: "raam" | "deur" | "leiding" | "vrij";
}
// Nieuw: verbindingen
interface ElementJoin {
id: string;
aId: string; aEnd: "start" | "end";
bId: string; bEnd: "start" | "end" | "path"; // path = T-verbinding
}
joins: ElementJoin[] // op Studio; export → IfcRelConnectsPathElementsapplyOpening() in meshBuilder generaliseert naar meerdere openingen + rond (cirkel benaderd met segmenten of boolean via three-bvh-csg — voorstel: rechthoek exact, rond via 16-segment benadering, geen CSG-dependency in v0.7).
- Multi-select: Ctrl-klik togglet, venster-selectie (links→rechts = omsluitend, rechts→links = kruisend, CAD-conventie), Esc deselecteert. Parameterpaneel toont gedeelde params bij homogene selectie.
- Klembord: Ctrl+C/X/V. Plakken volgt de cursor (preview) en bevestigt met klik; Ctrl+V nogmaals plakt opnieuw. Klembord serialiseert naar localStorage → plakken tussen twee open projecten werkt.
- Eind-handles (Tekla-les): geselecteerd lijnelement toont bolletjes op start/end; verslepen wijzigt alleen dat eindpunt (met element-snapping). Dit ís trim/extend voor 90 % van de gevallen.
- Element-snapping (voorwaarde voor 3, 5, 6): snappen op endpoints, midpoints en loodrechte projectie op assen van bestaande elementen; visuele snap-markers (vierkant = endpoint, driehoek = midpoint, ⊥-symbool). Raster- en stramien-snap blijven.
- Uitlijnen/spiegelen/reeks/offset: Align (klik doel-as, dan te verplaatsen element), Mirror (2 punten of stramienlijn, kopie-optie), Array (n × h.o.h. langs richting), Offset (afstand, kopie evenwijdig).
- Joins: automatisch bij snap-op-eindpunt (L) en snap-op-lijf (T) tijdens tekenen/handle-slepen; join beweegt mee (relatie herberekent eindpunten). Geometrisch: hoekverstek in bovenaanzicht door beide wandcontouren tot snijlijn door te zetten (verstek alleen tussen wanden met zelfde
depth-klasse; anders butt-joint). Handmatig: "Verbind"/"Verbreek verbinding"-knoppen. - Splitsen: klik op punt langs de as → twee elementen, params gekopieerd, openingen verdeeld op positie, joins blijven aan de buiteneinden.
- Match properties: penseel — klik bron, klik doel(en); neemt type-params over, niet lengte/positie.
- Sparingstool: klik op element → sparing op klikpunt met live preview; werkt op wanden én (nieuw) vloeren/daken (vlak-coördinaten). Kozijn plaatsen maakt automatisch een gekoppelde sparing (kind:"raam"/"deur") — vervangt de losse checkbox.
Nu ontstaat een "type" pas bij IFC-export (hash van template+params). Voorstel:
interface TypeDefinition {
id: string;
name: string; // "HSB-wand 396 Rc4.7 — variant B"
templateId: string;
typeParams: ParamValues; // de vastgezette parameters
}- Opslag: in het projectbestand én (optioneel, beslispunt 4) in een gebruikersbibliotheek (appdata) zodat typen projecten overstijgen.
- Aanmaken: (a) "Opslaan als type" vanaf geselecteerd element; (b) "Dupliceer type" in het parameterpaneel (Revit-patroon: dupliceren → naam → params wijzigen); (c) bestaande presets (.o3st-parameterpresets) migreren hiernaartoe.
- Gebruik: de kiezer wordt getrapte selectie hoofdcategorie → template → type; plaatsen zet
typeIdop het element; type-param wijzigen werkt door op álle instanties (met melding hoeveel). - Export:
IfcWallTypee.d. krijgen de typenaam i.p.v. het gegenereerde volgnummer; de bestaande type-groepering blijft als vangnet voor elementen zonder typeId. - Relatie met .o3st: een
.o3stblijft een template (parametrische definitie); een type is een instelling daarvan. Type exporteren als deelbaar bestand = klein.o3sty-JSON met templateId-referentie.
Eén vaste lijst MainCategory, afgeleid van NL-SfB-hoofdgroep (override per template mogelijk):
| MainCategory | NL-SfB | Huidige categorie-strings die erin opgaan |
|---|---|---|
| Fundering | 13/16/17 | Fundering |
| Wanden | 21/22 | Buitenwanden, Binnenwanden, Roosterwanden, Dragers, Roosters |
| Vloeren | 23 | Vloeren |
| Trappen & hellingen | 24 | Trappen |
| Daken | 27 | Daken |
| Draagconstructie | 28 | Hoofddraagconstructie |
| Kozijnen & deuren | 31/32 | Buitenkozijnen, Buitendeuren, Binnendeuren |
| Balustrades & leuningen | 34 | Balustrades |
| Afwerkingen | 4x | Gevelafwerking, Gevelbekleding, Vloerafwerking, Plafonds |
| Installaties | 5x/6x | alle "MEP — …" |
| Ruimten | 90 | Ruimten |
| Import & eigen | — | "Import — ", plugin-templates |
Binnen/buiten (21 vs 22) wordt subcategorie/filter binnen Wanden, zoals Revit's Function-parameter — één plek voor al het wandgereedschap, met de NL-SfB-code als het harde onderscheid dat blijft bestaan voor ILS/export.
- Kiezer: getrapt — hoofdcategorie-tabs met daarbinnen template → type (einde van de platte 52+-dropdown).
- Ribbon "Tekenen": één knop per hoofdcategorie (Wand · Vloer · Dak · Kolom/Ligger · Kozijn · Trap · Installatie …), Revit-patroon: klik = teken met laatst gebruikte type in die categorie; pijltje = variant kiezen.
- Lagenpaneel: hoofdcategorieën als lagen (12 i.p.v. 24 losse strings), uitklapbaar naar subcategorie.
- Category-defaults: default
placementKind, snapping-gedrag (kozijn snapt op wand-as, vloer op contour), parameterpaneel-groepering en sparingstool-gedrag per categorie. - Datamodel:
mainCategory?: MainCategoryop ComponentTemplate; ontbreekt hij, dan afleiden uit NL-SfB. Bestaandecategory-string blijft als weergave-subcategorie — geen breaking change voor templates of .o3st-bestanden.
| Sprint | Inhoud | Duur |
|---|---|---|
| S1 | Multi-select (Ctrl, venster) + klembord (C/X/V, cross-project) + bulk verwijderen/dupliceren | 2 w |
| S2 | Element-snapping (endpoint/midpoint/⊥) + eind-handles + uitlijnen + spiegelen + reeks + offset | 2–3 w |
| S3 | Joins (L/T, automatisch + handmatig, meebewegend) + splitsen + IfcRelConnectsPathElements |
2–3 w |
| S4 | Openingen 2.0: meerdere per element, rond+rechthoek, vloeren/daken, sparingstool, kozijn→sparing-koppeling | 2 w |
| S5 | Typen: TypeDefinition, opslaan-als/dupliceren, typenbrowser, doorwerking op instanties, IfcType-namen | 2 w |
| S6 | Hoofdcategorieën: taxonomie + getrapte kiezer + ribbon-knoppen + lagen op hoofdcategorie | 1–2 w |
Volgorde-rationale: S2 (snapping) is voorwaarde voor S3; S6 kan desgewenst naar voren omdat hij onafhankelijk is (beslispunt 7).
- Wanden één hoofdcategorie (21+22 samen, binnen/buiten als filter — Revit-model) of gescheiden houden?
- Joins als meebewegende relatie (voorstel; exporteert naar IFC) of eenmalige geometrische operatie (simpeler, breekt bij verslepen)?
- Klembord cross-project via localStorage (voorstel) of alleen binnen de sessie?
- Typen ook in centrale gebruikersbibliotheek (naast projectbestand) of alleen per project?
- Ribbon-knoppen per hoofdcategorie (Revit-stijl, voorstel) of de getrapte kiezer alleen in het zijpaneel?
- Openingen v0.7: rechthoek + rond (voorstel), vrije polygoon naar v0.8?
- Volgorde: S1→S6 zoals voorgesteld, of hoofdcategorieën (S6) éérst als organiserend fundament?
- Ronde sparingen: 16-segment-benadering (geen dependency, voorstel) of echte CSG via three-bvh-csg (mooier, +1 dependency, zwaarder)?