Everything in a world is a part. A part is the language's one unit of organization, as a module is in Cone: a named place that holds whatever belongs there. It may hold child parts, fields, materials, functions, triggers, layouts, images, sounds and files such as a JPEG. What a part holds decides what it is. A part with a frame (and usually a shape) stands somewhere in the world: the lantern, the cottage, the land. A part without a frame is a container: materials, which holds the world's materials, or a design not itself placed. The parts nest into one tree, and a part's place in that tree, written as a path, is its name, its identity and the line along which it is owned.
What a part is
A part is not a type and not an object in Cone's sense. It is closest to a folder that can also stand in the world. Four facts follow from that:
- It holds things of many kinds. A lantern holds its glass and bulb (child parts), its size and gain (fields), the formulas that tint it (subscriptions), its nearness rule (an event) and the area round its post (a thing the part is). A cottage holds its porch, which holds two wall lanterns. The world holds the lamp level and the rule that fades it at dusk.
- What it holds decides what it is. Nothing declares a part "placed" or "a container". A part with a frame is placed; one without is not. A part with a shape is drawn; one with an area senses; one with a seat can be sat on (frames.html).
- It is handed to the engines piece by piece. When the world loads, the engine takes each thing a part holds and gives it to the system that runs it: its events to the subscription and event engine, its frame to placement, its shapes to drawing and to physics, its sounds to the mixer. Game engines call this arrangement ECS, "entity, component, system": an entity is only an id, its components are plain data, and each system runs every entity that has the components it reads. The part is the entity; what it holds are its components.
- Its lifetime follows its holder. Whatever a part holds lasts as long as the part. A material held by one lantern goes when the lantern goes; one held by the world lasts as long as the world.
How you write it
A part is declared with part and its name, then optionally : what, then optionally named options separated by commas. Its contents (fields, child parts, rules and calls) are indented under it. A line may continue after a trailing comma, indented past the word part.
- What (after the colon) says what the part is: a call that makes a shape (
panes(size)), a light (pointLight(...)), a sound, an effect, a material or a spine, or a design to copy (designs.html). A copy names the design:wallLantern, orlanternHead(panel: p)with field values. A material may be a list, a base material followed by its modifiers:part lanternGlass: [shiny(srgb(255, 205, 130), highlight: 0.45, shininess: 30), translucent(covers: 0.12), glowing(color: glassColor, strength: glow)]. - Options are named by the argument rule (Expressions) and are listed under Part options: where the part is, how it is turned, its material.
part cottage, at: (-16, land.height((-16, 0, 330)), 330) // a one-off, placed on the land
part porch
part wallLantern1: wallLantern, at: (-0.7, 2.4, 0)
part wallLantern2: wallLantern, at: (0.85, 2.4, 0)
part materials // a part with no frame: a holder
part lanternGlass: ...
part lanternBulb : ...
The wall lanterns' places are in the porch's frame, which is in the cottage's. Each material is itself a part holding a material, under the frameless holder materials; a drawn part names one with material: materials.lanternGlass (materials.html). Two places are two parts: a figure's eyes are part leftEye: ..., at: body.bone(leftEye) and part rightEye: ....
Part options
The options follow the colon and the what, in this order when several are given. Each has a default, so each is named.
| Option | Type and default | Meaning |
|---|---|---|
material: | material (a material part, materials.lanternGlass) | the material a drawn part is made of |
at: | a place, or an attachment such as around(camera, rotation: world), body.bone(head), nearest(course, to: ear); default the parent's origin | where the part is, in its parent's frame (frames.html) |
rotation: | rotation = noRotation | how the part is turned: rotation: rotation(0.3 rad), rotation: rotation(a, about: axis), or a rotation value |
scale: | number or (x, y, z) = 1 | the part's size relative to its parent: one number for every axis, or a tuple of three for x, y and z in turn |
unscaled: | bool = false | true: the part's meters are true meters again, whatever the scale above it (Frames) |
castsShadow: | bool = true | whether the part throws shadows |
home: | a place | the place the part is brought back to (Frames: a part's home) |
clearance: | meters = 0 m | the room the part needs: a scatter keeps clear of it (Generators: what a scatter means). The amphora's is clearance: 1.2 m, the balloon's 12 m |
detailBySize: | list of pixel sizes | the apparent sizes at which coarser builds of the part take over, stated as a requirement: detailBySize: [240 px, 80 px] draws the part from its second build under 240 px across the screen and its third under 80 px (Construction: detail by size) |
visibleUpTo:, fadeFrom: | meters; fraction | how far the part must be drawn, and the share of that distance (from its far end back) over which it fades out: visibleUpTo: 140 m, fadeFrom: 0.7 keeps it whole to 98 m and fades it out by 140 m. The engine may draw it farther, never nearer |
lives: | client | server | both | where the part exists (Distribution) |
A part of a particular kind takes options of its own, in the same place. A land, which draws its own ground, takes material: like any drawn part and also edge: (what lies past the border of its grid) and fineOver: (where its ground is drawn finest): part land, at: (0, 0, 0), material: shaded(terrain), edge: sea(wade: 0.6 m) (Terrain).
Every drawn part also has a field tint, a color whose default is white, which a derive or a set may write: derive(glass.tint, gain) (Materials: the tint). It multiplies a painted material's colors too, and its components may pass 1. A tree's age tint is its part's tint, and its leaves' registration takes the part's.
part glass: panes(size), material: materials.lanternGlass, at: hangsAt
part bulb : filament(size), material: materials.lanternBulb, at: hangsAt, castsShadow: false
if let Some(p) = panel {
part solar: scale(p, by: size), material: panelIn, at: hangsAt
}
part light: pointLight(color: color(1, 0.14, 0.01), source: 2 cm, reach: 5 m, castsShadow: true),
at: hangsAt + (0, 0.335 m * size, 0)
The balloon, with a turned and scaled envelope and a basket scaled with it, its platform and area written in the envelope's units (meterUnit is a meter in those units, 1/9):
part envelope: balloonEnvelope(), material: paints.envelopePaint,
at: (0, -bottom, 0), rotation: rotation(0.3 rad), scale: 9
part basket: scale(wickerBasket(), by: meterUnit), material: paints.wicker
platform(self, floor: floorTop * meterUnit, standOn: box(x: -inside * meterUnit ..= inside * meterUnit, z: -inside * meterUnit ..= outside * meterUnit),
walls: (wallTop - floorTop) * meterUnit, on: [left, right, back])
area(self, box(x: -inside * meterUnit ..= inside * meterUnit, y: (floorTop - 0.1 m) * meterUnit ..= (floorTop + 1.9 m) * meterUnit,
z: -inside * meterUnit ..= outside * meterUnit))
Engine registrations are calls in the part's body that name the part. What a part is handed to an engine as (an area that senses, a platform to stand on, a body that moves, a being that can be possessed) is written as a call in the part's body whose first argument is the part, as body(self, ...), solid(self, ...) and seat(frame, ...) already are. The calls are body, solid, area, platform, seat and being; the others that fit a part to another one are written the same way: fittedTo(self, body), skinnedTo(self, body), under(self, trousers), grownOn(self, body.head), foliage(self, X, material: m). foliage registers the tree's leaves, drawn in the material m; trees that name the same material share it, and its tint is the part's tint.
area(p: part, volume: shape) platform(p: part, floor: meters, standOn: shape, walls: meters = 0 m, on: List[side] = []) being(p: part, possessable: bool = false, targetable: bool = true)
part post: stake(), material: materials.lanternBlack area(self, sphere(radius: 4 m, at: (0, 0.2, 0))) // the area round the post part villager: people.averageWoman // a design reached by a path being(self, possessable: true) // a visitor may take this body
Names and paths
A path names a part by the chain of its ancestors, joined by dots, the same dot that reaches a field:
island.cottage.porch.rockingChair pathLanterns.lantern[12].glass
- A name need only be unique among its siblings. Every lantern may have a part named
glass;cottage.porch.wallLantern1andcottage.kitchen.wallLantern1are different parts. - A bare name is found as in nested scopes. A bare name, or the first name of a path, is looked up in the current part (what it holds: its children, fields and functions), then in the part enclosing it, and so on outward to the world's root; the first match wins. So inside a lantern
glassis its own glass, whilelampLevelandmaterials.lanternBulbare found on the world's root, the first part outward that holds them. A nearer name hides a farther one of the same spelling. In a design, the search starts at the copy and goes outward through the parts that enclose the design's text (Expressions: how a name is found). - A path that starts with the world's own name is absolute, from the world's root:
island.materials.lanternBulb,island.labyrinth.gate. It is needed only where a nearer part of the same name would otherwise be found. A neighbor may also be namedself.parent.lantern[13], andself.parentalone is the part's parent. Relative names keep a design self-contained, so one lantern can be copied forty times and each copy'sglassmeans its own. - A field of a part is written with the same dot:
materials.lanternBulb.glow,cap.offset,island.cottage.open. A part's children, fields and functions are all its members, reached alike, so they share one set of names: a part may not have a child and a field of the same name. - Parts made in numbers by a loop are one of a numbered family, named with an index from the loop's values as a list's element is:
part lantern[2 * p + k]: ...declares one of the familylantern, andlantern[12]reads one (Generators). Parts spawned while the world runs are numbered the same way under their parent:westWall.splat[17]. A part named by hand (wallLantern1) keeps its name. - A part's children are listed by
p.parts: the parts directly under it, in the order they were declared, spawned ones after, so a rule may go through them witheach:each p in bowlingPins.parts { ... }. Every copy of a design, wherever held, iscopiesOf(design)(Designs: finding every copy).
The path separator / is also Cone's division sign: i / 20 divides, army.troll[0] is a path. This is listed under Open questions.
Folders and files
- A world is a directory, and folders on disk map to paths, as Cone's modules map to source files. A file sits beside the part that uses it.
- A file's declarations belong to the part its folder maps to: the world's root for a file in the world's top folder. A file adds a part under another part by declaring it with a path:
part cottage.porch.wallLantern1: wallLantern, at: (-0.7, 2.4, 0) part cottage.porch.wallLantern2: wallLantern, at: (0.85, 2.4, 0)
The porch itself is declared once, in the cottage's text; this file only adds to it. A file in the world's top folder adds to the world's root: its fields, rules and parts are the world's own, as if written at the top of the world's text. The lanterns' file is one, since it holds the world's lamp level and the rules that fade it as well as the lantern designs:field lampLevel: fraction = 0, within: 0 ..= 1, mutable: true // the world's own, from the top folder on(dusk, start(transition(lampLevel, to: 1, over: world(15 min), ease: linear, from: current, begin: dusk.due))) part lanternHead ...
The lanterns' file can then hold every lantern, wherever each hangs. Two declarations of the same path (awallLantern1here and another in the cottage's own text) are an error when the world is loaded, naming both places. - Files are the escape hatch, recipes the default. A world describes most things as recipes the browser builds (a lathe profile, a granite pattern from a seed). A file (a photograph, a recording) is used where no recipe will do. Files are stored by the hash of their content, so one image used by two worlds downloads once.
What it means
A path is a chain of frames
A part's position, rotation and size are stated relative to its parent and composed down the path. An office placed 10 m aft in a spaceship stays there: when the ship flies and turns, the office moves and turns with it, and nothing in the office's description changes. A frameless part's children are placed relative to the nearest ancestor that has a frame, the world at the top; under the hypothesis of scenes, the chain stops at the part's scene, since each scene has its own coordinate system (Worlds, scenes and regions). The kinds of frame are on frames.html.
A path carries ownership
What happens to a part happens to everything under it: removing it, showing or hiding it, putting it to sleep as a region, drawing its distant form in place of its parts. Removing the boss removes the army it holds; hiding the cottage hides its porch and wall lanterns. A simpler version of one mesh drawn further away (per-mesh level of detail) stays the engine's choice; what a whole hierarchy looks like from far away is the designer's intent, stated as content (Construction: distant forms).
Any part can be a region
A region is not a kind of part. Any part can be one: a part whose contents may be absent from a visitor's machine, with a stand-in drawn while they are. Regions nest: a city, a district in it, a house's interior in that. They wake when they might be seen (Worlds, scenes and regions).
A path is identity, not location
When a rider stands in the balloon's basket, being carried is execution state: the engine moves the rider with the basket, but the rider's path does not change. In the same way a door swung open has a current placement different from its defined one. Who owns a part's current place, the content or an engine, is on frames.html.
The world is the top part
The world itself is the root of the tree. World state (the lamp level, which weather is in force) is the root's fields; world settings (how long the day is) are its fixed fields; world rules (the lamps' dusk fade, the spells) are its events. There is no separate kind of "world script". Under the hypothesis of scenes, the world's children are its scenes, and the world itself holds what they share: its rules, state that survives moving between scenes, and what a visitor carries from one to another (Worlds: the world above its scenes).
Shared things are parts held somewhere
A material used by many parts is not a special "shared definition". Its type is material; sharing is only several parts referring to one instance, as several references point at one struct. The instance has identity and may have changing fields: one bulb material, its glow derived once, can light every lantern of two types. It lives wherever it is held (the world's root, a design, one lantern), lasts as long as its holder, and anyone refers to it by path. The materials / root is a convention of arrangement, not of meaning.
The tree may grow and shrink while the world runs
A part's set of children may change as the world runs: a wall hit by paint gains splats, a boss gains an army. That set is state of the parent, living on the client, the server or both like any other field. The rest follows from rules already stated: the new part's name is a numbered path under the parent, its frame and ownership are the parent's, and its lifetime is a handler or an animator's end (actions.html, spawn and remove).
A part's children are drawn only while it is. A child is present when its parent is, and gone when its parent is gone: a flock of birds that is a child of a sky is not drawn where that sky is not (the labyrinth, a scene with no sky), and a lantern's glass goes with the lantern. This is the lifetime-by-holder rule applied to what is drawn, and needs no line of content.
Two namespaces
A world's own names are paths in its tree. Beside them are the vocabulary names the browser supplies: lathe, pointLight, dusk, npcbehave.patrol. A world never names a package to import; the browser finds vocabulary names where they are used. Shape tools are written bare (lathe, box, loftAlong), never with their package's name (Appendix: shape tools). A world's own name is found first (above), so a world should not give its own parts or shapes the tools' names. A local name hides a tool of the same name. A world's own shape face, or a part glass, is what face and glass mean where it is in scope. The tool is still reached through its package: sculpt.face is the tool, whatever the world has called its own.
What a part cannot do
- It cannot hold a reference into the engine's memory. World logic holds only values and ids.
- It never names a player. Visitors are outside the content; a being a visitor drives is reached by relation (visitors.html).
- It never states resources: how a part is stored, streamed or culled is below the content. A part may state the detail it needs, as requirements the engine meets or exceeds (
detailBySize,visibleUpTo,fadeFrom), never less. Saying that a part is a region, where it can be seen from, and what it looks like from afar are not resource choices but design intent (Worlds, scenes and regions).
How it connects
- Fields: the values a part holds, own or common, fixed or changing.
- Designs, copies and spawning: any part can be copied by naming it.
- Frames: the kinds of frame, and what each registers with.
- Worlds, scenes and regions: how the tree under the world is partitioned; Portals: the joins between scenes.
- Loading: what a part declares is built; what its
on(created, ...)does is started. - Events and subscriptions: the logic a part holds.
- Generators: parts made in numbers, and their paths.
- The vocabulary: the names a world does not define.
Open questions
- Places as names. A villager's round names places (the cottage's front, the bridge's crest) that are not parts. Whether a named place is a part with no shape, or something else, is not stated.
- Persistence. Which server state survives a restart and which resets.
- Naming a grandchild. Rules written on the balloon must name
envelope.basket.capandenvelope.burner.flamein full, while the reference pages writeclicked(cap)andflame.firingas if a grandchild were found by its own name. Either a name is searched for below the part (and must be unique there), or paths are written out (The balloon ride).