[{"data":1,"prerenderedAt":729},["ShallowReactive",2],{"$fCMRVtjhr8S_6sQ6ID2LaEgHli7LnREwKQCuU_VyYGgY":3},{"newThing":4,"project":5,"cycle":9,"sections":21,"threads":677,"otherDocs":678,"documents":679,"images":680,"archiveItems":726,"links":727},"Virtual-grid layout engine — recursive bin packing decoupled from rendering",{"id":6,"label":7,"liveBase":8},"qmap","Qmap",null,{"id":10,"num":11,"dir":12,"name":13,"title":14,"focus":8,"status":15,"statusNote":8,"created":16,"completed":8,"mode":8,"report":8,"recordless":17,"thumbnail":18,"imageCount":11,"artifactCount":19,"lastActivity":20,"hasPickup":17,"hasInitialPrompt":17},"c11",11,"c11.grid-layout","grid-layout","Grid Layout Engine","archived","2026-03-23",false,"\u002Fcycles\u002Fqmap\u002Fc11-deep-nested.png",0,"2026-08-15",[22,101,118,165,178,394,650],{"key":23,"title":24,"empty":17,"ast":25},"extra","Why This Cycle Exists",{"type":26,"children":27},"root",[28,36,61,66,77],{"type":29,"tag":30,"props":31,"children":32},"element","p",{},[33],{"type":34,"value":35},"text","Previous layout engines (c5-c10) evolved organically, mixing concerns:",{"type":29,"tag":37,"props":38,"children":39},"ul",{},[40,46,51,56],{"type":29,"tag":41,"props":42,"children":43},"li",{},[44],{"type":34,"value":45},"Layout math (positions, bounds)",{"type":29,"tag":41,"props":47,"children":48},{},[49],{"type":34,"value":50},"Three.js coordinate conversion",{"type":29,"tag":41,"props":52,"children":53},{},[54],{"type":34,"value":55},"Collision resolution",{"type":29,"tag":41,"props":57,"children":58},{},[59],{"type":34,"value":60},"Label width estimation",{"type":29,"tag":30,"props":62,"children":63},{},[64],{"type":34,"value":65},"This created tight coupling and made the code hard to reason about.",{"type":29,"tag":30,"props":67,"children":68},{},[69,75],{"type":29,"tag":70,"props":71,"children":72},"strong",{},[73],{"type":34,"value":74},"C11 takes it back to basics:",{"type":34,"value":76}," build a clean, pure layout engine that:",{"type":29,"tag":78,"props":79,"children":80},"ol",{},[81,86,91,96],{"type":29,"tag":41,"props":82,"children":83},{},[84],{"type":34,"value":85},"Works in virtual grid units (not pixels or Three.js coordinates)",{"type":29,"tag":41,"props":87,"children":88},{},[89],{"type":34,"value":90},"Supports recursive nesting (groups of groups)",{"type":29,"tag":41,"props":92,"children":93},{},[94],{"type":34,"value":95},"Calculates size inside-out (deepest items first)",{"type":29,"tag":41,"props":97,"children":98},{},[99],{"type":34,"value":100},"Has a clear separation from the rendering layer",{"key":23,"title":102,"empty":17,"ast":103},"Architecture",{"type":26,"children":104},[105],{"type":29,"tag":106,"props":107,"children":111},"pre",{"className":108,"code":110,"language":34},[109],"language-text","layout\u002F\n├── LayoutItem.js      # Base class: id, width, height, x, y, children\n├── ActorItem.js       # Leaf node (1x1 grid unit)\n├── GroupItem.js       # Container with bin packing\n└── LayoutEngine.js    # Tree builder + computation\n\nview\u002F\n└── ViewOrchestrator.js  # Grid → Three.js translation\n",[112],{"type":29,"tag":113,"props":114,"children":116},"code",{"__ignoreMap":115},"",[117],{"type":34,"value":110},{"key":23,"title":119,"empty":17,"ast":120},"Key Design Decisions",{"type":26,"children":121},[122],{"type":29,"tag":78,"props":123,"children":124},{},[125,135,145,155],{"type":29,"tag":41,"props":126,"children":127},{},[128,133],{"type":29,"tag":70,"props":129,"children":130},{},[131],{"type":34,"value":132},"Uniform actor sizes",{"type":34,"value":134},": All actors are 1x1 grid units (simplifies bin packing)",{"type":29,"tag":41,"props":136,"children":137},{},[138,143],{"type":29,"tag":70,"props":139,"children":140},{},[141],{"type":34,"value":142},"Groups of groups",{"type":34,"value":144},": Multi-level nesting supported",{"type":29,"tag":41,"props":146,"children":147},{},[148,153],{"type":29,"tag":70,"props":149,"children":150},{},[151],{"type":34,"value":152},"Shelf bin packing",{"type":34,"value":154},": Children arranged in rows, sorted by height",{"type":29,"tag":41,"props":156,"children":157},{},[158,163],{"type":29,"tag":70,"props":159,"children":160},{},[161],{"type":34,"value":162},"Grid scale",{"type":34,"value":164},": 1 grid unit = 1.5 Three.js units (configurable)",{"key":23,"title":166,"empty":17,"ast":167},"Data Flow",{"type":26,"children":168},[169],{"type":29,"tag":106,"props":170,"children":173},{"className":171,"code":172,"language":34},[109],"actors + groupBy config\n    ↓\nLayoutEngine.buildTree()  → nested LayoutItems\n    ↓\nLayoutEngine.compute()    → inside-out size calculation\n    ↓\nflat positions in grid units\n    ↓\nViewOrchestrator          → Three.js world coordinates\n    ↓\npositioned meshes & rugs\n",[174],{"type":29,"tag":113,"props":175,"children":176},{"__ignoreMap":115},[177],{"type":34,"value":172},{"key":23,"title":179,"empty":17,"ast":180},"Test Battery",{"type":26,"children":181},[182,187,381],{"type":29,"tag":30,"props":183,"children":184},{},[185],{"type":34,"value":186},"Seven scenarios for validating layout behavior at different scales:",{"type":29,"tag":188,"props":189,"children":190},"table",{},[191,220],{"type":29,"tag":192,"props":193,"children":194},"thead",{},[195],{"type":29,"tag":196,"props":197,"children":198},"tr",{},[199,205,210,215],{"type":29,"tag":200,"props":201,"children":202},"th",{},[203],{"type":34,"value":204},"Scenario",{"type":29,"tag":200,"props":206,"children":207},{},[208],{"type":34,"value":209},"Actors",{"type":29,"tag":200,"props":211,"children":212},{},[213],{"type":34,"value":214},"Nesting",{"type":29,"tag":200,"props":216,"children":217},{},[218],{"type":34,"value":219},"Purpose",{"type":29,"tag":221,"props":222,"children":223},"tbody",{},[224,248,270,292,315,337,359],{"type":29,"tag":196,"props":225,"children":226},{},[227,233,238,243],{"type":29,"tag":228,"props":229,"children":230},"td",{},[231],{"type":34,"value":232},"Tiny",{"type":29,"tag":228,"props":234,"children":235},{},[236],{"type":34,"value":237},"3",{"type":29,"tag":228,"props":239,"children":240},{},[241],{"type":34,"value":242},"1-level",{"type":29,"tag":228,"props":244,"children":245},{},[246],{"type":34,"value":247},"Basic sanity check",{"type":29,"tag":196,"props":249,"children":250},{},[251,256,261,265],{"type":29,"tag":228,"props":252,"children":253},{},[254],{"type":34,"value":255},"Small",{"type":29,"tag":228,"props":257,"children":258},{},[259],{"type":34,"value":260},"13",{"type":29,"tag":228,"props":262,"children":263},{},[264],{"type":34,"value":242},{"type":29,"tag":228,"props":266,"children":267},{},[268],{"type":34,"value":269},"Simple multi-group",{"type":29,"tag":196,"props":271,"children":272},{},[273,278,283,287],{"type":29,"tag":228,"props":274,"children":275},{},[276],{"type":34,"value":277},"Medium",{"type":29,"tag":228,"props":279,"children":280},{},[281],{"type":34,"value":282},"30",{"type":29,"tag":228,"props":284,"children":285},{},[286],{"type":34,"value":242},{"type":29,"tag":228,"props":288,"children":289},{},[290],{"type":34,"value":291},"Moderate scale",{"type":29,"tag":196,"props":293,"children":294},{},[295,300,305,310],{"type":29,"tag":228,"props":296,"children":297},{},[298],{"type":34,"value":299},"Nested",{"type":29,"tag":228,"props":301,"children":302},{},[303],{"type":34,"value":304},"18",{"type":29,"tag":228,"props":306,"children":307},{},[308],{"type":34,"value":309},"2-level",{"type":29,"tag":228,"props":311,"children":312},{},[313],{"type":34,"value":314},"Groups within groups",{"type":29,"tag":196,"props":316,"children":317},{},[318,323,328,332],{"type":29,"tag":228,"props":319,"children":320},{},[321],{"type":34,"value":322},"Unbalanced",{"type":29,"tag":228,"props":324,"children":325},{},[326],{"type":34,"value":327},"25",{"type":29,"tag":228,"props":329,"children":330},{},[331],{"type":34,"value":242},{"type":29,"tag":228,"props":333,"children":334},{},[335],{"type":34,"value":336},"Extreme size variance (1 vs 15 items)",{"type":29,"tag":196,"props":338,"children":339},{},[340,345,350,354],{"type":29,"tag":228,"props":341,"children":342},{},[343],{"type":34,"value":344},"Large",{"type":29,"tag":228,"props":346,"children":347},{},[348],{"type":34,"value":349},"100",{"type":29,"tag":228,"props":351,"children":352},{},[353],{"type":34,"value":242},{"type":29,"tag":228,"props":355,"children":356},{},[357],{"type":34,"value":358},"Performance stress test",{"type":29,"tag":196,"props":360,"children":361},{},[362,367,371,376],{"type":29,"tag":228,"props":363,"children":364},{},[365],{"type":34,"value":366},"Deep Nested",{"type":29,"tag":228,"props":368,"children":369},{},[370],{"type":34,"value":282},{"type":29,"tag":228,"props":372,"children":373},{},[374],{"type":34,"value":375},"3-level",{"type":29,"tag":228,"props":377,"children":378},{},[379],{"type":34,"value":380},"Deep hierarchy",{"type":29,"tag":30,"props":382,"children":383},{},[384,386,392],{"type":34,"value":385},"See ",{"type":29,"tag":113,"props":387,"children":389},{"className":388},[],[390],{"type":34,"value":391},"shared\u002FtestScenarios.js",{"type":34,"value":393}," for implementations.",{"key":23,"title":395,"empty":17,"ast":396},"Learnings",{"type":26,"children":397},[398,405,423,441,451,462,467,473,478,486,491,497,507,517,565,591,599,605,614,623,632,640,645],{"type":29,"tag":399,"props":400,"children":402},"h3",{"id":401},"threejs-object-removal-pattern",[403],{"type":34,"value":404},"Three.js Object Removal Pattern",{"type":29,"tag":30,"props":406,"children":407},{},[408,413,415,421],{"type":29,"tag":70,"props":409,"children":410},{},[411],{"type":34,"value":412},"Problem",{"type":34,"value":414},": When switching scenarios, old actors remained visible even though ",{"type":29,"tag":113,"props":416,"children":418},{"className":417},[],[419],{"type":34,"value":420},"actorMeshes",{"type":34,"value":422}," was cleared.",{"type":29,"tag":30,"props":424,"children":425},{},[426,431,433,439],{"type":29,"tag":70,"props":427,"children":428},{},[429],{"type":34,"value":430},"Root cause",{"type":34,"value":432},": SceneManager wrapper methods (",{"type":29,"tag":113,"props":434,"children":436},{"className":435},[],[437],{"type":34,"value":438},"sceneManager.value.add\u002Fremove",{"type":34,"value":440},") don't always work consistently with Vue's reactivity system. Objects were being added but removal failed silently.",{"type":29,"tag":30,"props":442,"children":443},{},[444,449],{"type":29,"tag":70,"props":445,"children":446},{},[447],{"type":34,"value":448},"Solution",{"type":34,"value":450},": Use parent-based removal instead of trying to remove from a specific scene reference:",{"type":29,"tag":106,"props":452,"children":457},{"className":453,"code":454,"language":455,"meta":115,"style":456},"language-javascript","\u002F\u002F Instead of this (unreliable):\nsceneManager.value.remove(entity.group)\n\n\u002F\u002F Use this (reliable):\nentity.group?.parent?.remove(entity.group)\n","javascript","undefined",[458],{"type":29,"tag":113,"props":459,"children":460},{"__ignoreMap":115},[461],{"type":34,"value":454},{"type":29,"tag":30,"props":463,"children":464},{},[465],{"type":34,"value":466},"This pattern works regardless of how the object was added, since it removes from whatever parent the object actually has.",{"type":29,"tag":399,"props":468,"children":470},{"id":469},"scenemanager-access-pattern",[471],{"type":34,"value":472},"SceneManager Access Pattern",{"type":29,"tag":30,"props":474,"children":475},{},[476],{"type":34,"value":477},"When you need the actual Three.js scene:",{"type":29,"tag":106,"props":479,"children":481},{"className":453,"code":480,"language":455,"meta":115,"style":456},"function getThreeScene() {\n  const sm = sceneManager.value\n  return sm?.getScene?.() || sm?.scene || sm\n}\n",[482],{"type":29,"tag":113,"props":483,"children":484},{"__ignoreMap":115},[485],{"type":34,"value":480},{"type":29,"tag":30,"props":487,"children":488},{},[489],{"type":34,"value":490},"This handles both the SceneManager wrapper and direct scene references.",{"type":29,"tag":399,"props":492,"children":494},{"id":493},"depth-based-rug-coloring",[495],{"type":34,"value":496},"Depth-Based Rug Coloring",{"type":29,"tag":30,"props":498,"children":499},{},[500,505],{"type":29,"tag":70,"props":501,"children":502},{},[503],{"type":34,"value":504},"Goal",{"type":34,"value":506},": Nested rugs should have different colors to visually distinguish hierarchy levels.",{"type":29,"tag":30,"props":508,"children":509},{},[510,515],{"type":29,"tag":70,"props":511,"children":512},{},[513],{"type":34,"value":514},"Implementation",{"type":34,"value":516},":",{"type":29,"tag":78,"props":518,"children":519},{},[520,541,552],{"type":29,"tag":41,"props":521,"children":522},{},[523,525,531,533,539],{"type":34,"value":524},"Color scheme stored in ",{"type":29,"tag":113,"props":526,"children":528},{"className":527},[],[529],{"type":34,"value":530},"shared\u002FcolorScheme.js",{"type":34,"value":532}," with ",{"type":29,"tag":113,"props":534,"children":536},{"className":535},[],[537],{"type":34,"value":538},"rugDepth.fills[]",{"type":34,"value":540}," array",{"type":29,"tag":41,"props":542,"children":543},{},[544,550],{"type":29,"tag":113,"props":545,"children":547},{"className":546},[],[548],{"type":34,"value":549},"getRugColorByDepth(depth, type)",{"type":34,"value":551}," helper returns color by nesting level",{"type":29,"tag":41,"props":553,"children":554},{},[555,557,563],{"type":34,"value":556},"GroupRug accepts ",{"type":29,"tag":113,"props":558,"children":560},{"className":559},[],[561],{"type":34,"value":562},"depth_level",{"type":34,"value":564}," parameter and applies colors in constructor",{"type":29,"tag":30,"props":566,"children":567},{},[568,573,575,581,583,589],{"type":29,"tag":70,"props":569,"children":570},{},[571],{"type":34,"value":572},"Key insight - Root offset",{"type":34,"value":574},": The root group has ",{"type":29,"tag":113,"props":576,"children":578},{"className":577},[],[579],{"type":34,"value":580},"depth=0",{"type":34,"value":582}," but is never rendered (skipped in ViewOrchestrator). First visible groups have ",{"type":29,"tag":113,"props":584,"children":586},{"className":585},[],[587],{"type":34,"value":588},"item.depth=1",{"type":34,"value":590},". Subtract 1 so visible rugs start at color index 0:",{"type":29,"tag":106,"props":592,"children":594},{"className":453,"code":593,"language":455,"meta":115,"style":456},"const depthLevel = Math.max(0, item.depth - 1)\n",[595],{"type":29,"tag":113,"props":596,"children":597},{"__ignoreMap":115},[598],{"type":34,"value":593},{"type":29,"tag":399,"props":600,"children":602},{"id":601},"nested-rug-z-fighting",[603],{"type":34,"value":604},"Nested Rug Z-Fighting",{"type":29,"tag":30,"props":606,"children":607},{},[608,612],{"type":29,"tag":70,"props":609,"children":610},{},[611],{"type":34,"value":412},{"type":34,"value":613},": Sibling rugs at the same nesting level showed parent's color instead of their own. Console logs showed correct depth\u002Fcolor values, but visuals didn't match.",{"type":29,"tag":30,"props":615,"children":616},{},[617,621],{"type":29,"tag":70,"props":618,"children":619},{},[620],{"type":34,"value":430},{"type":34,"value":622},": All rugs were positioned at the same Y height (0.01), causing z-fighting. Three.js couldn't determine which surface to render on top.",{"type":29,"tag":30,"props":624,"children":625},{},[626,630],{"type":29,"tag":70,"props":627,"children":628},{},[629],{"type":34,"value":448},{"type":34,"value":631},": Stack deeper rugs slightly higher and use render order:",{"type":29,"tag":106,"props":633,"children":635},{"className":453,"code":634,"language":455,"meta":115,"style":456},"\u002F\u002F In GroupRug._createPlane():\nthis.plane.position.y = 0.01 + this.depthLevel * 0.001;\nthis.plane.renderOrder = this.depthLevel;\n\n\u002F\u002F In GroupRug._createBorder():\nconst y = 0.02 + this.depthLevel * 0.001;\nthis.border.renderOrder = this.depthLevel + 10;\n",[636],{"type":29,"tag":113,"props":637,"children":638},{"__ignoreMap":115},[639],{"type":34,"value":634},{"type":29,"tag":30,"props":641,"children":642},{},[643],{"type":34,"value":644},"Child rugs now render properly on top of parent rugs.",{"type":29,"tag":646,"props":647,"children":648},"style",{},[649],{"type":34,"value":115},{"key":23,"title":651,"empty":17,"ast":652},"What's Next",{"type":26,"children":653},[654,659],{"type":29,"tag":30,"props":655,"children":656},{},[657],{"type":34,"value":658},"Once the core layout engine is proven:",{"type":29,"tag":37,"props":660,"children":661},{},[662,667,672],{"type":29,"tag":41,"props":663,"children":664},{},[665],{"type":34,"value":666},"Apply this pattern retroactively to c9\u002Fc10",{"type":29,"tag":41,"props":668,"children":669},{},[670],{"type":34,"value":671},"Add morph animations between layouts",{"type":29,"tag":41,"props":673,"children":674},{},[675],{"type":34,"value":676},"Explore alternative packing strategies",[],[],[],[681,688,692,695,699,702,706,710,714,718,722],{"src":682,"kind":683,"order":684,"caption":685,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-skewed-regions.png","hero",78,"Skewed Regions (the default stress case)","2026-08-16T03:06:59.862Z","\u002Fexperiments\u002Fc11.grid-layout\u002F",{"src":689,"kind":683,"order":690,"caption":691,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-tiny.png",79,"Tiny — the engine at minimum",{"src":693,"kind":683,"order":694,"caption":277,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-medium.png",80,{"src":696,"kind":683,"order":697,"caption":698,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-nested.png",81,"Nested containers",{"src":18,"kind":683,"order":700,"caption":701,"capturedAt":686,"route":687},82,"Deep Nested — recursion visible",{"src":703,"kind":683,"order":704,"caption":705,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-microservices.png",83,"Microservices topology",{"src":707,"kind":683,"order":708,"caption":709,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-enterprise.png",84,"Enterprise scale",{"src":711,"kind":683,"order":712,"caption":713,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-massive.png",85,"Massive — the packing holds",{"src":715,"kind":683,"order":716,"caption":717,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-fractal.png",86,"Fractal 3x3x3x3 — pure structure",{"src":719,"kind":683,"order":720,"caption":721,"capturedAt":686,"route":687},"\u002Fcycles\u002Fqmap\u002Fc11-org-chaos.png",87,"Org Chaos — the honest enterprise",{"src":723,"kind":724,"order":725},"\u002Fcycles\u002Fqmap\u002Fc11.outcomes.png","legacy",100,[],[728],{"route":687,"href":8},1794599369691]