From 9adfbbe8f19dc5a3a3accde8b891cb9472b70172 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Sun, 26 Apr 2026 10:43:11 +0930 Subject: [PATCH 01/11] Improve documentation grouping and content --- docs/source/api/light.rst | 48 +++++++++-- docs/source/api/style.rst | 75 +++++++++-------- docs/source/builders/luadoc.py | 62 +++++++++++++- docs/source/builders/luastruct.py | 121 +++++++++++++++++++-------- docs/source/chapters_config.json | 86 ++++++++++++++++++++ docs/source/manual/assets.rst | 87 +++++++++++++++++++- docs/source/manual/input.rst | 94 +++++++++++++++++++++ docs/source/manual/physics2d.rst | 128 ++++++++++++++++++++++++++++- docs/source/manual/physics3d.rst | 130 ++++++++++++++++++++++++++++++ docs/source/manual/scenes.rst | 124 ++++++++++++++++++++++++++-- docs/source/manual/sound.rst | 91 +++++++++++++++++++++ 11 files changed, 954 insertions(+), 92 deletions(-) create mode 100644 docs/source/chapters_config.json diff --git a/docs/source/api/light.rst b/docs/source/api/light.rst index b22682d..8967015 100644 --- a/docs/source/api/light.rst +++ b/docs/source/api/light.rst @@ -43,31 +43,63 @@ Casts a light source onto the 3D environment. Can be used with basic immediate m .. lua:staticmethod:: light.directional([direction]) - Create a new directional light + Create a new directional light. Directional lights simulate a distant light source (like the sun) that casts parallel rays in a given direction and supports shadow mapping. + + :param direction: The direction vector the light casts in world space, defaults to ``vec3(0, -1, 0)`` + :type direction: vec3 + + :return: A new directional light + :rtype: light + + .. code-block:: lua + + sun = scn:entity("sun") + sunLight = sun:add(light.directional(vec3(0, -1, 0))) + sunLight.castShadows = true .. helptext:: create a new directional light .. lua:staticmethod:: light.point([position]) - Create a new positional light + Create a new positional point light that radiates in all directions from a given position. + + *Note: point lights are not currently supported by the renderer* + + :param position: The world-space position of the light source, defaults to the origin + :type position: vec3 - *note: currently not supported by the renderer* + :return: A new point light + :rtype: light .. helptext:: create a new point light .. lua:staticmethod:: light.spot([position, direction]) - Create a new positional light + Create a new spot light that emits a cone of light from a position toward a direction. - *note: currently not supported by the renderer* + *Note: spot lights are not currently supported by the renderer* + + :param position: The world-space position of the light source, defaults to the origin + :type position: vec3 + :param direction: The direction the cone points in world space, defaults to ``vec3(0, -1, 0)`` + :type direction: vec3 + + :return: A new spot light + :rtype: light .. helptext:: create a new spot light .. lua:staticmethod:: light.push(light) - Pushes a light source onto the rendering stack - - This will apply basic lighting to 3D objects with lit materials after the light has been pushed but will not support advanced features such as shadows + Pushes a light source onto the immediate mode rendering stack. + + This applies basic lighting to 3D objects with lit materials for all subsequent draw calls until ``light.pop()`` is called. Immediate mode lighting does not support advanced features such as shadows — use scene-based lights for those. + + :param light: The light to push onto the rendering stack + :type light: light + + :return: The same light for chaining + :rtype: light .. helptext:: push a light source onto the rendering stack diff --git a/docs/source/api/style.rst b/docs/source/api/style.rst index 3f121b6..4817ecd 100644 --- a/docs/source/api/style.rst +++ b/docs/source/api/style.rst @@ -483,6 +483,31 @@ Clipping Stencil ####### +.. lua:function:: stencil(state) + stencil() + + Sets/gets the current stencil state for both front and back faces + + .. helptext:: set the stencil state for front and back faces + +.. lua:function:: stencil(front, back) + + Sets the current stencil state for front and back faces separately + + .. helptext:: set separate stencil states for front and back faces + +Using Stencils +************** + +A stencil state is configured using a table with the following properties: + +* ``reference`` +* ``condition`` +* ``readMask`` +* ``pass`` +* ``fail`` +* ``zfail`` + .. code-block:: lua :caption: A simple mask effect using stencils @@ -490,19 +515,19 @@ Stencil background(40, 40, 50) -- When a pixel is drawn write 1 to the stencil buffer - style.stencil - { - reference = 1, - pass = STENCIL_OP_REPLACE + style.stencil + { + reference = 1, + pass = STENCIL_OP_REPLACE } - - -- Use opacity clip to only draw pixels when alpha is great than .99 + + -- Use opacity clip to only draw pixels when alpha is greater than .99 style.opacityClip(0.99) style.blend(DISABLED) -- no blending needed matrix.push().transform2d(CurrentTouch.x, CurrentTouch.y, 1, 1, time.elapsed * 50) sprite(asset.builtin.Cargo_Bot.Codea_Icon, 0, 0, 400) matrix.pop() - + style.blend(NORMAL) style.noOpacityClip() -- Only draw if stencil is equal to one using the equal test condition @@ -511,40 +536,11 @@ Stencil reference = 1, condition = STENCIL_TEST_EQUAL } - -- This sets the line thickness sprite(asset.builtin.SpaceCute.Beetle_Ship, WIDTH/2, HEIGHT/2, 400) end -Stencils are configured using a table with the following properties: - -* ``reference`` -* ``condition`` -* ``readMask`` -* ``pass`` -* ``fail`` -* ``zfail`` - -.. lua:function:: stencil(state) - stencil() - - Sets/gets the current stencil state for both front and back faces - - .. helptext:: set the stencil state for front and back faces - -.. lua:function:: stencil(front, back) - - Sets/gets the current stencil state for both front and back faces - - .. helptext:: set separate stencil states for front and back faces - - - -Constants - Stencil -******************* - -Used by drawing commands and shaders to control stencil operations - -**Stencil Test (conditions)** +Stencil Test +************ .. lua:attribute:: STENCIL_TEST_LESS: const @@ -571,7 +567,8 @@ Used by drawing commands and shaders to control stencil operations .. helptext:: always stencil test constant -**Stencil Operations (pass, fail, zfail)** +Stencil Operations +****************** .. lua:attribute:: STENCIL_OP_ZERO: const diff --git a/docs/source/builders/luadoc.py b/docs/source/builders/luadoc.py index 783a4b4..f025760 100644 --- a/docs/source/builders/luadoc.py +++ b/docs/source/builders/luadoc.py @@ -35,12 +35,35 @@ def write_doc(self, docname, doctree): with open(path, 'w') as f: json.dump(data, f, indent=2) + def finish(self): + config_path = os.path.join(self.env.srcdir, 'chapters_config.json') + if not os.path.exists(config_path): + return + with open(config_path, 'r') as f: + chapters_config = json.load(f) + chapters_out = [] + for chapter in chapters_config: + chapters_out.append({ + 'id': chapter['id'], + 'title': chapter['title'], + 'subtitle': chapter['subtitle'], + 'icon': chapter.get('icon'), + 'sources': [f"{e}.json" for e in chapter.get('entries', [])] + }) + out_path = os.path.join(self.outdir, 'chapters.json') + print(f"Writing chapters index to {out_path}") + with open(out_path, 'w') as f: + json.dump(chapters_out, f, indent=2) + class LuaJSONVisitor(nodes.NodeVisitor): def __init__(self, builder, doc): super().__init__(doc) self.entries = [] self.class_stack = [] - self.current_group = None + self.document_group = None # top-level (file) title + self.section_group = None # level-2 section title — used as group for sub-section overviews + self.current_group = None # current group for API entries + self.current_name = None # subsection title — used as name for overview entries self.current_section_content = [] def has_desc_ancestor(self, node): @@ -60,16 +83,49 @@ def visit_literal_block(self, node): if isinstance(node.parent, section) and not self.has_desc_ancestor(node): self.current_section_content.append(OverviewContent(node.astext(), OverviewContentKind.CODE)) + def visit_table(self, node): + if isinstance(node.parent, section) and not self.has_desc_ancestor(node): + title, header, rows = DocutilsUtils.extract_table(node) + self.current_section_content.append(OverviewContent({'title': title, 'header': header, 'rows': rows}, OverviewContentKind.TABLE)) + raise nodes.SkipNode + def visit_title(self, node): self.flush_content() - self.current_group = node.astext() + title_text = node.astext() + parent = node.parent + grandparent = parent.parent if parent else None + great_grandparent = grandparent.parent if grandparent else None + + is_top_level = isinstance(grandparent, nodes.document) if parent else False + is_level2 = (isinstance(grandparent, nodes.section) and + isinstance(great_grandparent, nodes.document)) if parent else False + + if is_top_level: + self.document_group = title_text + self.section_group = None + self.current_name = None + elif is_level2: + self.section_group = title_text + self.current_name = None + else: + self.current_name = title_text + self.current_group = title_text def depart_section(self, node): self.flush_content() def flush_content(self): if self.current_section_content: - self.entries.append(LuaOverview(self.current_section_content, self.current_group)) + if self.current_name is None: + # Content directly under a level-2 section (no sub-section entered yet). + # Group under the document title; name the overview after the section itself. + group = self.document_group or self.current_group + name = self.section_group + else: + # Content under a level-3+ sub-section — use the level-2 section as the group. + group = self.section_group or self.document_group or self.current_group + name = self.current_name + self.entries.append(LuaOverview(self.current_section_content, group, name)) self.current_section_content = [] def visit_text(self, node): diff --git a/docs/source/builders/luastruct.py b/docs/source/builders/luastruct.py index 21df142..2ae3e2a 100644 --- a/docs/source/builders/luastruct.py +++ b/docs/source/builders/luastruct.py @@ -91,31 +91,40 @@ def extract_parameters(node, isClass = False): if field_name and field_name.astext() == "Parameters": field_body = field.next_node(condition=lambda n: n.tagname == 'field_body') if field_body: + # Sphinx renders multiple params as bullet_list, single param as paragraph bullet_list = field_body.next_node(condition=lambda n: n.tagname == 'bullet_list') + param_paragraphs = [] if bullet_list: for list_item in bullet_list.children: - param_name_node = list_item.next_node(condition=lambda n: n.tagname == 'literal_strong') - param_description_nodes = list_item.next_node(condition=lambda n: n.tagname == 'paragraph') - - if param_name_node and param_description_nodes: - param_name = param_name_node.astext().split('=')[0].strip() # Handle default values here if specified - default_value = param_name_node.astext().split('=')[1].strip() if '=' in param_name_node.astext() else None - # Extract the type if available within parenthesis - param_type = None - description_text = param_description_nodes.astext() - - if ('(' in description_text and ')' in description_text): - start = description_text.find('(') + 1 - end = description_text.find(')') - param_type = description_text[start:end] - - # Description often follows the type enclosed in dash - param_description = description_text.split('–')[-1].strip() - param_details[param_name] = { - 'type': param_type, - 'description': param_description, - 'default': default_value - } + para = list_item.next_node(condition=lambda n: n.tagname == 'paragraph') + if para: + param_paragraphs.append(para) + else: + # Single parameter: field_body contains the paragraph directly + for child in field_body.children: + if child.tagname == 'paragraph': + param_paragraphs.append(child) + + for param_description_nodes in param_paragraphs: + param_name_node = param_description_nodes.next_node(condition=lambda n: n.tagname == 'literal_strong') + if param_name_node and param_description_nodes: + param_name = param_name_node.astext().split('=')[0].strip() + default_value = param_name_node.astext().split('=')[1].strip() if '=' in param_name_node.astext() else None + param_type = None + description_text = param_description_nodes.astext() + + if ('(' in description_text and ')' in description_text): + start = description_text.find('(') + 1 + end = description_text.find(')') + param_type = description_text[start:end] + + # Description follows the type, after the em-dash separator + param_description = description_text.split('–')[-1].strip() + param_details[param_name] = { + 'type': param_type, + 'description': param_description, + 'default': default_value + } if param_list and isClass == False: for child in param_list.children: @@ -174,7 +183,7 @@ def extract_code_samples(node): 'code': code }) - # Uncaptioned code blocks (plain .. code-block:: lua) appear as bare literal_block nodes + # Uncaptioned code blocks and collapsible sections inside desc_content desc_content = next((child for child in node.children if child.tagname == 'desc_content'), None) if desc_content: for child in desc_content.children: @@ -186,9 +195,46 @@ def extract_code_samples(node): 'title': '', 'code': code }) + elif child.__class__.__name__ == 'CollapseNode': + # Collapsible section (.. collapse:: Title) — use the label as the sample title + title = getattr(child, 'label', None) or 'Example' + for literal in child.traverse(condition=lambda n: n.tagname == 'literal_block'): + code = literal.astext() + if code not in seen_code: + seen_code.add(code) + code_samples.append({ + 'title': title, + 'code': code + }) return code_samples + @staticmethod + def extract_table(node): + title = None + header = [] + rows = [] + + title_node = next((child for child in node.children if child.tagname == 'title'), None) + if title_node: + title = title_node.astext() + + tgroup = next((child for child in node.traverse() if child.tagname == 'tgroup'), None) + if tgroup: + thead = next((child for child in tgroup.children if child.tagname == 'thead'), None) + if thead: + header_row = next((child for child in thead.children if child.tagname == 'row'), None) + if header_row: + header = [entry.astext() for entry in header_row.children if entry.tagname == 'entry'] + + tbody = next((child for child in tgroup.children if child.tagname == 'tbody'), None) + if tbody: + for row in tbody.children: + if row.tagname == 'row': + rows.append([entry.astext() for entry in row.children if entry.tagname == 'entry']) + + return title, header, rows + @staticmethod def extract_overview(node): desc_content = next((child for child in node.children if child.tagname == 'desc_content'), None) @@ -369,13 +415,14 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.module = DocutilsUtils.extract_module(node) self.syntax = DocutilsUtils.extract_syntax(node) self.examples = DocutilsUtils.extract_code_samples(node) + self.readonly = False + self.default_value = None self.type = self.extract_type(node) self.description = DocutilsUtils.extract_description(node) self.helptext = DocutilsUtils.extract_helptext(node) self.visibility = DocutilsUtils.extract_visibility(node) self.kind = kind self.group = group - self.default_value = None # Initializing default value else: # Initialize from provided parameters self.name = name @@ -386,23 +433,25 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.examples = examples self.helptext = helptext self.default_value = None + self.readonly = False self.visibility = visibility self.group = group self.kind = kind if kind else 'attribute' def extract_type(self, node): - # Finds the first 'desc_type' element and extracts its text, along with any default value if specified. + # Finds the first 'desc_type' element and extracts its text, stripping bracket qualifiers. type_node = next((child for child in node.traverse() if child.tagname == 'desc_type'), None) if type_node: type_text = type_node.astext() - # Check for default value pattern in the type text - if '[' in type_text and 'default' in type_text: - # Extract the type up to the '[' + if '[' in type_text: type_name = type_text[:type_text.find('[')].strip() - # Extract default value after 'default =' - default_start = type_text.find('default =') + len('default =') - default_end = type_text.find(']', default_start) - self.default_value = type_text[default_start:default_end].strip() + bracket_content = type_text[type_text.find('[')+1:type_text.find(']')].strip().lower() + if 'readonly' in bracket_content: + self.readonly = True + if 'default =' in bracket_content: + default_start = type_text.find('default =') + len('default =') + default_end = type_text.find(']', default_start) + self.default_value = type_text[default_start:default_end].strip() return type_name return type_text return None @@ -465,6 +514,7 @@ def to_dict(self): 'type': self.type, 'group': self.group, 'defaultValue': self.default_value, + 'readonly': self.readonly, 'description': self.description, 'helptext': self.helptext } @@ -474,9 +524,10 @@ def to_dict(self): class LuaOverview: - def __init__(self, content, group=None): + def __init__(self, content, group=None, name=None): self.content = content self.group = group + self.name = name # section title; group is the document/file title def __str__(self): return f"Overview\n\t{self.content}" @@ -485,12 +536,14 @@ def to_dict(self): return { 'kind': 'overview', 'content': [c.to_dict() for c in self.content], - 'group': self.group + 'group': self.group, + 'name': self.name } class OverviewContentKind(Enum): TEXT = "text" CODE = "code" + TABLE = "table" # Class to represent either code block or text content class OverviewContent: diff --git a/docs/source/chapters_config.json b/docs/source/chapters_config.json new file mode 100644 index 0000000..a209506 --- /dev/null +++ b/docs/source/chapters_config.json @@ -0,0 +1,86 @@ +[ + { + "id": "Graphics", + "title": "Graphics", + "subtitle": "Drawing shapes, images and styles in 2D and 3D", + "icon": "ChapterIconGraphics", + "entries": ["manual/drawing", "api/graphics", "api/style", "api/color", "api/image"] + }, + { + "id": "Scenes", + "title": "Scenes & Entities", + "subtitle": "Creating and managing 3D scenes, entities and cameras", + "icon": "ChapterIconCraft", + "entries": ["manual/scenes", "api/scene", "api/entity", "api/camera", "api/light"] + }, + { + "id": "Shaders", + "title": "Shaders & Meshes", + "subtitle": "3D mesh rendering, materials and GPU shaders", + "icon": "ChapterIconShaders", + "entries": ["manual/shaders", "api/mesh", "api/material", "api/shader", "api/gpu_noise_lib"] + }, + { + "id": "Physics", + "title": "Physics", + "subtitle": "Dynamic motion with forces, joints and collisions", + "icon": "ChapterIconPhysics", + "entries": ["manual/physics2d", "api/physics2d", "manual/physics3d", "api/physics3d"] + }, + { + "id": "Input", + "title": "Input", + "subtitle": "Responding to touches, keyboard and device motion", + "icon": "ChapterIconTouch", + "entries": ["manual/input", "api/input", "api/motion"] + }, + { + "id": "Sounds", + "title": "Sound", + "subtitle": "Playing and generating audio and sound effects", + "icon": "ChapterIconSounds", + "entries": ["manual/sound", "api/sound"] + }, + { + "id": "Storage", + "title": "Storage & Assets", + "subtitle": "Managing files, assets and persistent data", + "icon": "ChapterIconStorage", + "entries": ["manual/assets", "manual/file_operations", "api/file", "api/storage", "api/pick"] + }, + { + "id": "Vector", + "title": "Math & Types", + "subtitle": "Vector, matrix and mathematical types", + "icon": "ChapterIconVector", + "entries": ["api/math_types", "api/matrix"] + }, + { + "id": "Display", + "title": "UI & Viewer", + "subtitle": "User interface components and display settings", + "icon": "ChapterIconParameters", + "entries": ["api/ui", "api/viewer", "api/device", "api/inspector"] + }, + { + "id": "Animation", + "title": "Animation", + "subtitle": "Animating values and objects using tweens", + "icon": "ChapterIconAnimation", + "entries": ["api/tween"] + }, + { + "id": "Lua", + "title": "Lua Language", + "subtitle": "Tables, strings, math and Objective-C bridge", + "icon": "ChapterIconLua", + "entries": ["api/lua", "api/string", "api/require", "api/objc", "api/pasteboard"] + }, + { + "id": "Codea", + "title": "Codea", + "subtitle": "How Codea works — setup, draw, callbacks and lifecycle", + "icon": "ChapterIconDisplay", + "entries": ["manual/codea", "manual/codea_3x"] + } +] diff --git a/docs/source/manual/assets.rst b/docs/source/manual/assets.rst index d798624..a4bf036 100644 --- a/docs/source/manual/assets.rst +++ b/docs/source/manual/assets.rst @@ -2,10 +2,95 @@ Assets ====== Assets in Codea ----------------- +--------------- + +Assets are files — images, sounds, 3D models, text, JSON — stored in your project or in Codea's built-in libraries. You access them through *asset keys*, which are Lua values that represent a path within a specific asset library. + +.. code-block:: lua + + -- Load an image from the built-in Codea assets + myImage = readImage(asset.builtin.Cargo_Bot.Codea_Dark) + + -- Load from your own project + myImage = readImage(asset .. "MySprite.png") Asset Keys ---------- +An asset key is a pointer to a file in a specific asset library. Asset keys look like table fields but are resolved by Codea into full file paths at runtime. + +The main asset libraries are: + +- ``asset`` — files in your current project +- ``asset.documents`` — your personal Documents folder +- ``asset.builtin`` — read-only Codea built-in assets (sprites, sounds, shaders) +- ``asset.icloud`` — iCloud Drive documents (when signed in) + +You can navigate sub-folders using dot notation or the ``..`` operator: + +.. code-block:: lua + + -- Dot notation (for known names) + local key = asset.builtin.Cargo_Bot.Codea_Dark + + -- Concatenation (for dynamic names) + local filename = "MySprite.png" + local key = asset .. filename + + -- Combine paths + local key = asset.documents .. "Saves/Level1.json" + Loading and Saving Assets ------------------------- + +Use the standard reading functions with asset keys instead of file path strings: + +.. code-block:: lua + + -- Images + local img = readImage(asset.builtin.Cargo_Bot.Codea_Dark) + saveImage(asset.documents .. "Screenshot.png", img) + + -- Text + local text = readLocalData("highscore") + saveLocalData("highscore", 1000) + + -- JSON via the pick API + local tbl = pick.table() -- opens document picker, returns a table + +Asset keys also work with the :lua:class:`file` module for copying, moving, and deleting files. + +Working with Asset Packs +------------------------ + +Asset packs are folders that group related files together. You can inspect what's inside a pack using ``pairs()``: + +.. code-block:: lua + + -- List all built-in Cargo Bot assets + for name, key in pairs(asset.builtin.Cargo_Bot) do + print(name, key) + end + +You can create your own packs by creating subfolders inside your project. + +Bookmarks (References) +----------------------- + +When you pick a file from outside your project using ``pick.option.reference``, you receive an asset key that points to the original file. Because the file path may change between sessions, you must save a *bookmark* to reliably access it later: + +.. code-block:: lua + + -- Pick a file by reference and save a bookmark + local key = pick.asset(pick.option.reference) + if key then + key:saveBookmark("myConfig") + end + + -- On a later run, restore the bookmark + local key = assets.readBookmark("myConfig") + if key then + local text = read(key) + end + +Remove bookmarks when they're no longer needed with ``assets.removeBookmark("myConfig")``. diff --git a/docs/source/manual/input.rst b/docs/source/manual/input.rst index 2f3a510..dd3ab8a 100644 --- a/docs/source/manual/input.rst +++ b/docs/source/manual/input.rst @@ -4,14 +4,108 @@ Input Input in Codea -------------- +Codea provides a unified input system that handles touch, mouse, keyboard, and device motion across iOS and macOS. The main entry point is the :lua:mod:`input` module, which exposes current input state each frame. + +All input state is sampled once per frame at the start of ``draw()``. You can read it synchronously without callbacks. + Touches ------- +Touches represent screen contacts (fingers on iOS, mouse clicks on macOS). Use ``input.touches`` to get all current active touches: + +.. code-block:: lua + + function draw() + background(40) + + for id, touch in pairs(input.touches) do + fill(255, 100, 100) + circle(touch.x, touch.y, 30) + end + end + +Each touch object provides: + +- ``touch.x``, ``touch.y`` — current position +- ``touch.prevX``, ``touch.prevY`` — previous frame position +- ``touch.deltaX``, ``touch.deltaY`` — movement since last frame +- ``touch.began``, ``touch.moving``, ``touch.ended`` — phase booleans +- ``touch.tapCount`` — number of taps + +For callback-based touch handling, define the global ``touched(touch)`` function or set ``entity.touched`` on an entity: + +.. code-block:: lua + + function touched(touch) + if touch.began then + print("Touch started at", touch.x, touch.y) + end + end + Key Presses ----------- +Read keyboard state via ``input.key``: + +.. code-block:: lua + + function draw() + if input.key.w or input.key.up then + player.y = player.y + speed * DeltaTime + end + if input.key.s or input.key.down then + player.y = player.y - speed * DeltaTime + end + end + +Check if any key is pressed with ``input.key.pressed`` (returns true while any key is held). Special key names include ``space``, ``return``, ``backspace``, ``up``, ``down``, ``left``, ``right``, ``shift``, ``ctrl``, ``alt``, and ``cmd``. + +For text input, read the current text from ``input.keyboard.text`` and clear it each frame: + +.. code-block:: lua + + local inputBuffer = "" + + function draw() + if #input.keyboard.text > 0 then + inputBuffer = inputBuffer .. input.keyboard.text + input.keyboard.text = "" + end + end + Trackpad -------- +On macOS, the trackpad provides precise cursor input. Use ``input.mouse`` to read mouse / trackpad state: + +.. code-block:: lua + + function draw() + -- Cursor position + local x, y = input.mouse.x, input.mouse.y + + -- Button state + if input.mouse.left then + -- left button held + end + + -- Scroll delta + local scrollY = input.mouse.scroll.y + end + Hover ----- + +Detect when the pointer hovers over a region without clicking: + +.. code-block:: lua + + function draw() + local hovered = input.mouse.x > 50 and input.mouse.x < 150 + and input.mouse.y > 50 and input.mouse.y < 150 + + fill(hovered and color.yellow or color.white) + rect(50, 50, 100, 100) + end + +Entity-based input systems (with ``entity.hitTest = true``) automatically handle spatial hover and touch testing using attached collider shapes, removing the need for manual bounds checks. diff --git a/docs/source/manual/physics2d.rst b/docs/source/manual/physics2d.rst index b7bf2a8..69a2980 100644 --- a/docs/source/manual/physics2d.rst +++ b/docs/source/manual/physics2d.rst @@ -1,11 +1,135 @@ -2D Physics (Box 2D) -=================== +2D Physics (Box2D) +================== 2D Physics in Codea ------------------- +Codea's 2D physics system is built on Box2D. It simulates rigid body dynamics with forces, joints, sensors, and collision callbacks in the XY plane. + +2D physics can be used standalone with ``physics2d.world``, or integrated with the scene system using entity components. + Creating Physics Worlds ----------------------- +A standalone 2D physics world lets you manage the simulation and draw using the immediate mode API: + +.. code-block:: lua + + function setup() + world = physics2d.world() + world.gravity = vec2(0, -9.8) + + -- Static ground + ground = world:body(physics2d.STATIC) + ground:add(physics2d.box(WIDTH, 20)) + ground.position = vec2(WIDTH/2, 10) + + -- Dynamic circle + ball = world:body(physics2d.DYNAMIC) + ball:add(physics2d.circle(20)) + ball.position = vec2(WIDTH/2, HEIGHT/2) + end + + function update(dt) + world:update(dt) + end + + function draw() + background(40, 40, 50) + fill(255, 100, 100) + circle(ball.position.x, ball.position.y, 20) + end + +Body types: + +- ``physics2d.STATIC`` — immovable, acts as ground or walls +- ``physics2d.DYNAMIC`` — moved by forces and gravity +- ``physics2d.KINEMATIC`` — moved manually, pushes dynamic bodies + Using Physics with Entities --------------------------- + +Attach 2D physics components to scene entities for automatic simulation: + +.. code-block:: lua + + function setup() + scn = scene() + + -- Set up a 2D canvas camera + local cam = scn:entity("camera") + cam:add(camera.rigs.canvas) + + -- Ground + local ground = scn:entity("ground") + ground:add(physics2d.body(physics2d.STATIC)) + ground:add(physics2d.box(400, 20)) + ground.position = vec3(WIDTH/2, 10, 0) + + -- Ball + local ball = scn:entity("ball") + ball.sprite = asset.builtin.Blocks.Yellow_Circle + ball:add(physics2d.body(physics2d.DYNAMIC)) + ball:add(physics2d.circle(32)) + ball.position = vec3(WIDTH/2, HEIGHT/2, 0) + + scene.main = scn + end + +Collision Shapes +---------------- + +- ``physics2d.circle(radius)`` — circular collider +- ``physics2d.box(width, height)`` — rectangular collider +- ``physics2d.polygon(vertices)`` — arbitrary convex polygon, vertices as a table of vec2 +- ``physics2d.chain(vertices, loop)`` — open or closed chain of line segments (STATIC only) +- ``physics2d.edge(start, end)`` — single line segment (STATIC only) + +Collision Callbacks +------------------- + +React to collisions through entity callbacks: + +.. code-block:: lua + + local ball = scn:entity() + ball:add(physics2d.body(physics2d.DYNAMIC)) + ball:add(physics2d.circle(20)) + + ball.collisionBegan2d = function(contact) + print("Collision with impulse:", contact.normalImpulse) + end + +Sensors +------- + +Sensors detect overlaps without generating collision responses: + +.. code-block:: lua + + local trigger = scn:entity("trigger") + trigger:add(physics2d.body(physics2d.STATIC)) + local col = trigger:add(physics2d.circle(50)) + col.isSensor = true + + trigger.collisionBegan2d = function(contact) + print("Something entered the trigger zone") + end + +Forces and Impulses +------------------- + +Control dynamic bodies with forces and impulses: + +.. code-block:: lua + + local body = ent:get(physics2d.body) + + -- Push upward continuously (apply in update) + body:applyForce(vec2(0, 500)) + + -- Instant kick + body:applyImpulse(vec2(100, 0)) + + -- Set velocity directly + body.linearVelocity = vec2(5, 0) diff --git a/docs/source/manual/physics3d.rst b/docs/source/manual/physics3d.rst index 2bbf0f3..82f346e 100644 --- a/docs/source/manual/physics3d.rst +++ b/docs/source/manual/physics3d.rst @@ -4,8 +4,138 @@ 3D Physics in Codea ------------------- +Codea's 3D physics system is built on the Bullet physics engine. It provides rigid body dynamics with support for forces, impulses, joints, and collision events. + +3D physics can be used in two ways: standalone with ``physics3d.world``, or integrated with the scene system via entity components. + Creating Physics Worlds ----------------------- +A standalone physics world manages its own simulation loop: + +.. code-block:: lua + + function setup() + world = physics3d.world() + world.gravity = vec3(0, -9.8, 0) + + -- Create a ground plane + ground = world:body(physics3d.STATIC) + ground:add(physics3d.plane(vec3(0, 1, 0), 0)) + + -- Create a dynamic sphere + ball = world:body(physics3d.DYNAMIC) + ball:add(physics3d.sphere(0.5)) + ball.position = vec3(0, 5, 0) + end + + function update(dt) + world:update(dt) + end + + function draw() + -- Draw the ball position + pushMatrix() + translate(ball.position.x, ball.position.y, ball.position.z) + sphere(0.5) + popMatrix() + end + +Body types: + +- ``physics3d.STATIC`` — does not move, acts as immovable scenery +- ``physics3d.DYNAMIC`` — moved by forces and collisions +- ``physics3d.KINEMATIC`` — moved manually via code, pushes dynamic bodies without being affected by gravity + Using Physics with Entities --------------------------- + +When working with a :lua:class:`scene`, attach physics components directly to entities: + +.. code-block:: lua + + function setup() + scn = scene() + + local cam = scn:entity("camera") + cam:add(camera.perspective(60)) + cam:add(camera.rigs.orbit) + cam.z = -10 + + -- Static ground + local ground = scn:entity("ground") + ground:add(mesh.plane()) + ground:add(physics3d.body(physics3d.STATIC)) + ground:add(physics3d.box(vec3(10, 0.1, 10))) + ground.material = material.lit() + + -- Dynamic box + local box = scn:entity("box") + box:add(mesh.box()) + box:add(physics3d.body(physics3d.DYNAMIC)) + box:add(physics3d.box(vec3(1, 1, 1))) + box.y = 5 + box.material = material.lit() + + scene.main = scn + end + +The scene automatically steps the physics simulation each frame. + +Collision Shapes +---------------- + +Choose the collision shape that best matches your object's visual appearance. Simpler shapes perform better: + +- ``physics3d.sphere(radius)`` — sphere +- ``physics3d.box(halfExtents)`` — axis-aligned box, parameter is half-widths as a vec3 +- ``physics3d.capsule(radius, height)`` — capsule (good for characters) +- ``physics3d.cylinder(radius, height)`` — cylinder +- ``physics3d.cone(radius, height)`` — cone +- ``physics3d.plane(normal, constant)`` — infinite plane, STATIC only +- ``physics3d.hull(points)`` — convex hull around a set of points + +.. code-block:: lua + + body:add(physics3d.sphere(1)) + body:add(physics3d.box(vec3(0.5, 1, 0.5))) + body:add(physics3d.capsule(0.4, 1.8)) + +Collision Callbacks +------------------- + +React to collisions through entity callbacks or body callbacks: + +.. code-block:: lua + + local box = scn:entity() + box:add(physics3d.body(physics3d.DYNAMIC)) + box:add(physics3d.box(vec3(1,1,1))) + + box.collisionBegan3d = function(contact) + print("Hit something!", contact.impulse) + end + +The ``contact`` object contains ``impulse`` (force magnitude) and ``normal`` (collision normal vector). + +Forces and Impulses +------------------- + +Apply forces and impulses to dynamic bodies to set them in motion: + +.. code-block:: lua + + local body = ent:get(physics3d.body) + + -- Continuous force (applied each frame, use in update) + body:applyForce(vec3(0, 100, 0)) + + -- Instant impulse (applied once) + body:applyImpulse(vec3(0, 10, 0)) + + -- Torque (rotational force) + body:applyTorque(vec3(0, 5, 0)) + + -- Set velocity directly + body.linearVelocity = vec3(1, 0, 0) + body.angularVelocity = vec3(0, 1, 0) diff --git a/docs/source/manual/scenes.rst b/docs/source/manual/scenes.rst index eca8e3a..095757d 100644 --- a/docs/source/manual/scenes.rst +++ b/docs/source/manual/scenes.rst @@ -1,19 +1,133 @@ Scenes ====== -Codea features a high-level scene system that automates and integrates many -systems together, such as rendering physics and input. +Scenes in Codea +--------------- -Scenes can be saved and loaded +The :lua:class:`scene` system is the modern, high-level way to build 3D (and 2D) experiences in Codea. A scene manages a hierarchy of :lua:class:`entity` objects, each of which can have components — meshes, physics bodies, cameras, lights, custom Lua classes — attached to them. -Scenes in Codea ----------------- +Unlike the immediate-mode drawing API (``sprite``, ``rect``, etc.), scenes automatically handle the update loop, render pass, and physics simulation for you. + +.. code-block:: lua + + function setup() + scn = scene() + -- Camera entity + local cam = scn:entity("camera") + cam:add(camera.perspective(60)) + cam:add(camera.rigs.orbit) + cam.z = -10 + -- A sphere + local sphere = scn:entity("sphere") + sphere:add(mesh.sphere(1)) + -- Make this the active scene + scene.main = scn + end Creating Scenes --------------- +Create a new scene by calling :lua:class:`scene` as a constructor. Assign it to ``scene.main`` to start rendering it each frame. + +.. code-block:: lua + + scn = scene() + scene.main = scn + +You can have multiple scenes and switch between them by reassigning ``scene.main``. + Entities -------- + +Entities are the basic building blocks of a scene. They have a transform (position, rotation, scale) and can hold any number of components. + +Create entities with ``scene:entity()``: + +.. code-block:: lua + + -- Named entity (accessible by name later) + local ball = scn:entity("ball") + + -- Access by name + print(scn.ball) + +Child entities are created under an existing entity and inherit its transform: + +.. code-block:: lua + + local arm = scn:entity("arm") + local hand = arm:child("hand") + hand.z = 2 -- 2 units ahead of the arm's local origin + +Components +---------- + +Components add behaviour to entities. Attach them with :lua:meth:`entity.add`: + +.. code-block:: lua + + local sphere = scn:entity("sphere") + + -- Add a mesh + sphere:add(mesh.sphere(1)) + + -- Add a lit material + sphere.material = material.lit() + + -- Add a 3D physics body + sphere:add(physics3d.body(physics3d.DYNAMIC)) + sphere:add(physics3d.sphere(1)) + +Custom Lua classes are also valid components: + +.. code-block:: lua + + Rotator = class("Rotator") + + function Rotator:update(dt) + self.entity.ry = self.entity.ry + dt * 90 + end + + local cube = scn:entity() + cube:add(mesh.box()) + cube:add(Rotator) + +The Scene Camera +---------------- + +Every scene needs at least one camera entity with a :lua:class:`camera` component attached: + +.. code-block:: lua + + local cam = scn:entity("camera") + cam:add(camera.perspective(60)) -- 60° field of view + cam.z = -5 -- Pull back 5 units + +Use ``camera.rigs.orbit`` to add an interactive orbit controller that works with mouse and touch: + +.. code-block:: lua + + cam:add(camera.rigs.orbit) + +Lifecycle Callbacks +------------------- + +Entities respond to lifecycle callbacks that mirror Codea's global callbacks. Define them as properties or override them in Lua component classes: + +.. code-block:: lua + + local ent = scn:entity() + + ent.update = function(dt) + ent.ry = ent.ry + dt * 45 + end + + ent.touched = function(touch) + if touch.began then + ent:destroy() + end + return true -- capture the touch + end diff --git a/docs/source/manual/sound.rst b/docs/source/manual/sound.rst index 40f1c85..4b995e4 100644 --- a/docs/source/manual/sound.rst +++ b/docs/source/manual/sound.rst @@ -1,2 +1,93 @@ Sound ===== + +Sound in Codea +-------------- + +Codea's :lua:mod:`sound` module lets you play audio files, synthesise sound effects procedurally, and control playback. + +Playing Audio Files +------------------- + +Play a file from the project or built-in assets with ``sound.play``: + +.. code-block:: lua + + -- Play a built-in sound + sound.play(asset.builtin.A_Spaceship.Laser_Shoot) + + -- Play a project sound and control it + local src = sound.play(asset .. "music.mp3", { loop = true, volume = 0.5 }) + +``sound.play`` returns a ``sound.source`` that lets you control the audio after it starts: + +.. code-block:: lua + + local src = sound.play(asset .. "music.mp3", { loop = true }) + + -- Pause and resume + src:pause() + src:play() + + -- Adjust volume and pitch at runtime + src.volume = 0.3 + src.pitch = 1.2 + + -- Stop completely + src:stop() + +Loading vs Playing +------------------ + +For sounds you play frequently (like a gun shot), load the source once in ``setup()`` and call ``play()`` on it: + +.. code-block:: lua + + function setup() + shootSound = sound.load(asset.builtin.A_Spaceship.Laser_Shoot) + end + + function draw() + if input.key.space then + shootSound:play() + end + end + +Using ``sound.load`` is more efficient than calling ``sound.play`` every frame because the audio data is decoded once. + +Procedural Sound +---------------- + +Generate synthesised sounds at runtime without any audio file using ``sound.synthesize``: + +.. code-block:: lua + + -- A retro blip sound + local blip = sound.synthesize({ + wave = SINE, + startFreq = 440, + endFreq = 880, + sustainTime = 0.1, + decayTime = 0.05, + volume = 0.8 + }) + blip:play() + +Parameters let you shape the sound's frequency envelope, waveform, and timing. + +Background Music +---------------- + +For longer music tracks, use ``sound.play`` with ``{ loop = true }``. Keep a reference to stop or fade it: + +.. code-block:: lua + + function setup() + music = sound.play(asset .. "background.mp3", { loop = true, volume = 0.4 }) + end + + function cleanup() + if music then + music:stop() + end + end From baa8cb655a039e15ae2b5ef80076f40e1c450fcf Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Sun, 26 Apr 2026 11:40:18 +0930 Subject: [PATCH 02/11] Improves vector docs --- docs/source/api/math_types.rst | 742 +++++++++++++++++++++++++++------ 1 file changed, 607 insertions(+), 135 deletions(-) diff --git a/docs/source/api/math_types.rst b/docs/source/api/math_types.rst index bd638f6..558fe4a 100644 --- a/docs/source/api/math_types.rst +++ b/docs/source/api/math_types.rst @@ -1,12 +1,12 @@ -math types -========== +Math +==== 2D Vectors ########## .. lua:class:: vec2 - This type represents a 2D vector. Most mathematical operators such as equality, addition, subtraction, multiplication and division are provided, so you can use ``vec2`` data types similarly to how you use numerical types. In addition there are a number of methods, such as ``v:dot( vec2 )`` that can be called on vec2 types. + This type represents a 2D vector. Most mathematical operators such as equality, addition, subtraction, multiplication and division are provided, so you can use ``vec2`` data types similarly to how you use numerical types. :param x: Initial x value of the vector :type x: number @@ -28,7 +28,7 @@ math types .. lua:attribute:: y: number - The y component of this vector + The y component of this vector .. helptext:: get or set the y component @@ -36,11 +36,25 @@ math types Return a ``vec2`` containing the component-wise minimum of two vectors + :param v1: The first vector + :type v1: vec2 + :param v2: The second vector + :type v2: vec2 + :return: A new ``vec2`` with the minimum of each component + :rtype: vec2 + .. helptext:: return the component-wise minimum of two vectors .. lua:staticmethod:: max(v1, v2) - Return a ``vec2`` containing the component-wise maximum two vectors + Return a ``vec2`` containing the component-wise maximum of two vectors + + :param v1: The first vector + :type v1: vec2 + :param v2: The second vector + :type v2: vec2 + :return: A new ``vec2`` with the maximum of each component + :rtype: vec2 .. helptext:: return the component-wise maximum of two vectors @@ -58,86 +72,146 @@ math types .. lua:method:: normalize() - Normalize this vector + Normalize this vector in-place .. helptext:: normalize this vector - .. lua:method:: normalized() + .. lua:method:: normalized() -> vec2 Return a normalized copy of this vector + :return: A normalized copy of this vector + :rtype: vec2 + .. helptext:: return a normalized copy of this vector - .. lua:method:: dot(v) + .. lua:method:: dot(v) -> number Perform a scalar dot product with another vector and return the result + :param v: The other vector + :type v: vec2 + :return: The scalar dot product + :rtype: number + .. helptext:: calculate the dot product with another vector - .. lua:method:: distance(v) + .. lua:method:: distance(v) -> number - Calculate the distance (i.e. L1 norm) to another vector + Calculate the Euclidean distance to another vector + + :param v: The other vector + :type v: vec2 + :return: The distance between the two vectors + :rtype: number .. helptext:: calculate the distance to another vector - .. lua:method:: distance2(v) + .. lua:method:: distance2(v) -> number + + Calculate the squared Euclidean distance to another vector - Calculate the squared distance (i.e. L2 norm) to another vector + :param v: The other vector + :type v: vec2 + :return: The squared distance between the two vectors + :rtype: number .. helptext:: calculate the squared distance to another vector - .. lua:method:: reflect(normal) + .. lua:method:: reflect(normal) -> vec2 - Reflect this vector about another + Reflect this vector about a normal + + :param normal: The normal vector to reflect about + :type normal: vec2 + :return: The reflected vector + :rtype: vec2 .. helptext:: reflect this vector about a normal - .. lua:method:: refract(normal, ior) + .. lua:method:: refract(normal, ior) -> vec2 - Refract this vector though another with a given index or refraction + Refract this vector through a surface with a given index of refraction + + :param normal: The surface normal + :type normal: vec2 + :param ior: The index of refraction + :type ior: number + :return: The refracted vector + :rtype: vec2 .. helptext:: refract this vector through a normal - .. lua:method:: lerp(v, t) + .. lua:method:: lerp(v, t) -> vec2 + + Interpolate this vector with another by a given factor - Interpolate this vector with another by the a given factor (typically between 0 and 1) + :param v: The target vector + :type v: vec2 + :param t: The interpolation factor (typically between 0 and 1) + :type t: number + :return: The interpolated vector + :rtype: vec2 .. helptext:: linearly interpolate this vector with another - .. lua:method:: abs() + .. lua:method:: abs() -> vec2 Return a copy of this vector with component-wise absolute values + :return: A copy with all components made positive + :rtype: vec2 + .. helptext:: return a copy with component-wise absolute values - .. lua:method:: unpack() -> x, y + .. lua:method:: unpack() -> number, number - Unpack this vector as multiple number values + Unpack this vector as individual number values + + :return: The x and y components as separate values .. helptext:: unpack this vector as multiple numbers - - .. lua:method:: cross(v) - Perform a cross product with another vec2 and return the result + .. lua:method:: cross(v) -> number + + Compute the 2D cross product (perp dot product) with another vector + + :param v: The other vector + :type v: vec2 + :return: The scalar cross product result + :rtype: number .. helptext:: calculate the cross product with another vector - - .. lua:method:: rotate(angleRadians) + + .. lua:method:: rotate(angleRadians) -> vec2 Rotate this vector by a given angle in radians + :param angleRadians: The angle of rotation in radians + :type angleRadians: number + :return: The rotated vector + :rtype: vec2 + .. helptext:: rotate this vector by an angle in radians - - .. lua:method:: rotate90() + + .. lua:method:: rotate90() -> vec2 Rotate this vector by 90 degrees + :return: The rotated vector + :rtype: vec2 + .. helptext:: rotate this vector by 90 degrees - - .. lua:method:: angleBetween(v) + + .. lua:method:: angleBetween(v) -> number Calculate the oriented angle between this vector and another, between -pi and pi + :param v: The other vector + :type v: vec2 + :return: The oriented angle in radians between -pi and pi + :rtype: number + .. helptext:: calculate the oriented angle between this vector and another 3D Vectors @@ -148,7 +222,14 @@ math types .. lua:staticmethod:: vec3(x) vec3(x, y, z) - Create a new ``vec3`` by setting all values at once or each one individually + Create a new ``vec3`` by setting all components to the same value, or each one individually + + :param x: The x component (also used for y and z when called with a single argument) + :type x: number + :param y: The y component + :type y: number + :param z: The z component + :type z: number .. helptext:: create a new vec3 @@ -156,11 +237,25 @@ math types Return a ``vec3`` containing the component-wise minimum of two vectors + :param v1: The first vector + :type v1: vec3 + :param v2: The second vector + :type v2: vec3 + :return: A new ``vec3`` with the minimum of each component + :rtype: vec3 + .. helptext:: return the component-wise minimum of two vectors .. lua:staticmethod:: max(v1, v2) - Return a ``vec3`` containing the component-wise maximum two vectors + Return a ``vec3`` containing the component-wise maximum of two vectors + + :param v1: The first vector + :type v1: vec3 + :param v2: The second vector + :type v2: vec3 + :return: A new ``vec3`` with the maximum of each component + :rtype: vec3 .. helptext:: return the component-wise maximum of two vectors @@ -196,67 +291,114 @@ math types .. lua:method:: normalize() - Normalize this vector + Normalize this vector in-place .. helptext:: normalize this vector - .. lua:method:: normalized() + .. lua:method:: normalized() -> vec3 Return a normalized copy of this vector + :return: A normalized copy of this vector + :rtype: vec3 + .. helptext:: return a normalized copy of this vector - .. lua:method:: dot(v) + .. lua:method:: dot(v) -> number Perform a scalar dot product with another vector and return the result + :param v: The other vector + :type v: vec3 + :return: The scalar dot product + :rtype: number + .. helptext:: calculate the dot product with another vector - .. lua:method:: cross(v) + .. lua:method:: cross(v) -> vec3 - Perform a cross product with another vec3 and return the result + Perform a cross product with another ``vec3`` and return the result + + :param v: The other vector + :type v: vec3 + :return: A vector perpendicular to both input vectors + :rtype: vec3 .. helptext:: calculate the cross product with another vector - .. lua:method:: distance(v) + .. lua:method:: distance(v) -> number + + Calculate the Euclidean distance to another vector - Calculate the distance (i.e. L1 norm) to another vector + :param v: The other vector + :type v: vec3 + :return: The distance between the two vectors + :rtype: number .. helptext:: calculate the distance to another vector - .. lua:method:: distance2(v) + .. lua:method:: distance2(v) -> number - Calculate the squared distance (i.e. L2 norm) to another vector + Calculate the squared Euclidean distance to another vector + + :param v: The other vector + :type v: vec3 + :return: The squared distance between the two vectors + :rtype: number .. helptext:: calculate the squared distance to another vector - .. lua:method:: reflect(normal) + .. lua:method:: reflect(normal) -> vec3 + + Reflect this vector about a normal - Reflect this vector about another + :param normal: The normal vector to reflect about + :type normal: vec3 + :return: The reflected vector + :rtype: vec3 .. helptext:: reflect this vector about a normal - .. lua:method:: refract(normal, ior) + .. lua:method:: refract(normal, ior) -> vec3 + + Refract this vector through a surface with a given index of refraction - Refract this vector though another with a given index or refraction + :param normal: The surface normal + :type normal: vec3 + :param ior: The index of refraction + :type ior: number + :return: The refracted vector + :rtype: vec3 .. helptext:: refract this vector through a normal - .. lua:method:: lerp(v, t) + .. lua:method:: lerp(v, t) -> vec3 - Interpolate this vector with another by the a given factor (typically between 0 and 1) + Interpolate this vector with another by a given factor + + :param v: The target vector + :type v: vec3 + :param t: The interpolation factor (typically between 0 and 1) + :type t: number + :return: The interpolated vector + :rtype: vec3 .. helptext:: linearly interpolate this vector with another - .. lua:method:: abs() + .. lua:method:: abs() -> vec3 Return a copy of this vector with component-wise absolute values + :return: A copy with all components made positive + :rtype: vec3 + .. helptext:: return a copy with component-wise absolute values - .. lua:method:: unpack() -> x, y, z + .. lua:method:: unpack() -> number, number, number + + Unpack this vector as individual number values - Unpack this vector as multiple number values + :return: The x, y and z components as separate values .. helptext:: unpack this vector as multiple numbers @@ -268,7 +410,16 @@ math types .. lua:staticmethod:: vec4(x) vec4(x, y, z, w) - Create a new ``vec4`` by setting all values at once or each one individually + Create a new ``vec4`` by setting all components to the same value, or each one individually + + :param x: The x component (also used for y, z and w when called with a single argument) + :type x: number + :param y: The y component + :type y: number + :param z: The z component + :type z: number + :param w: The w component + :type w: number .. helptext:: create a new vec4 @@ -276,11 +427,25 @@ math types Return a ``vec4`` containing the component-wise minimum of two vectors + :param v1: The first vector + :type v1: vec4 + :param v2: The second vector + :type v2: vec4 + :return: A new ``vec4`` with the minimum of each component + :rtype: vec4 + .. helptext:: return the component-wise minimum of two vectors .. lua:staticmethod:: max(v1, v2) - Return a ``vec4`` containing the component-wise maximum two vectors + Return a ``vec4`` containing the component-wise maximum of two vectors + + :param v1: The first vector + :type v1: vec4 + :param v2: The second vector + :type v2: vec4 + :return: A new ``vec4`` with the maximum of each component + :rtype: vec4 .. helptext:: return the component-wise maximum of two vectors @@ -322,61 +487,103 @@ math types .. lua:method:: normalize() - Normalize this vector + Normalize this vector in-place .. helptext:: normalize this vector - .. lua:method:: normalized() + .. lua:method:: normalized() -> vec4 Return a normalized copy of this vector + :return: A normalized copy of this vector + :rtype: vec4 + .. helptext:: return a normalized copy of this vector - .. lua:method:: dot(v) + .. lua:method:: dot(v) -> number Perform a scalar dot product with another vector and return the result + :param v: The other vector + :type v: vec4 + :return: The scalar dot product + :rtype: number + .. helptext:: calculate the dot product with another vector - .. lua:method:: distance(v) + .. lua:method:: distance(v) -> number + + Calculate the Euclidean distance to another vector - Calculate the distance (i.e. L1 norm) to another vector + :param v: The other vector + :type v: vec4 + :return: The distance between the two vectors + :rtype: number .. helptext:: calculate the distance to another vector - .. lua:method:: distance2(v) + .. lua:method:: distance2(v) -> number + + Calculate the squared Euclidean distance to another vector - Calculate the squared distance (i.e. L2 norm) to another vector + :param v: The other vector + :type v: vec4 + :return: The squared distance between the two vectors + :rtype: number .. helptext:: calculate the squared distance to another vector - .. lua:method:: reflect(normal) + .. lua:method:: reflect(normal) -> vec4 - Reflect this vector about another + Reflect this vector about a normal + + :param normal: The normal vector to reflect about + :type normal: vec4 + :return: The reflected vector + :rtype: vec4 .. helptext:: reflect this vector about a normal - .. lua:method:: refract(normal, ior) + .. lua:method:: refract(normal, ior) -> vec4 + + Refract this vector through a surface with a given index of refraction - Refract this vector though another with a given index or refraction + :param normal: The surface normal + :type normal: vec4 + :param ior: The index of refraction + :type ior: number + :return: The refracted vector + :rtype: vec4 .. helptext:: refract this vector through a normal - .. lua:method:: lerp(v, t) + .. lua:method:: lerp(v, t) -> vec4 - Interpolate this vector with another by the a given factor (typically between 0 and 1) + Interpolate this vector with another by a given factor + + :param v: The target vector + :type v: vec4 + :param t: The interpolation factor (typically between 0 and 1) + :type t: number + :return: The interpolated vector + :rtype: vec4 .. helptext:: linearly interpolate this vector with another - .. lua:method:: abs() + .. lua:method:: abs() -> vec4 Return a copy of this vector with component-wise absolute values + :return: A copy with all components made positive + :rtype: vec4 + .. helptext:: return a copy with component-wise absolute values - .. lua:method:: unpack() -> x, y, z, w + .. lua:method:: unpack() -> number, number, number, number + + Unpack this vector as individual number values - Unpack this vector as multiple number values + :return: The x, y, z and w components as separate values .. helptext:: unpack this vector as multiple numbers @@ -399,7 +606,6 @@ Vector Swizzling v1.yx = vec2(5, 6) -- v1 is now '(6.0, 5.0, 3.0, 4.0)' v1.xyz = v2.yzx -- v1 is now '(6.0, 7.0, 5.0, 4.0)' - Quaternions ########### @@ -411,101 +617,137 @@ Quaternions Create a new ``quat`` + :param w: The scalar (real) component + :type w: number + :param x: The x imaginary component + :type x: number + :param y: The y imaginary component + :type y: number + :param z: The z imaginary component + :type z: number + .. helptext:: create a new quaternion .. lua:staticmethod:: lookRotation(forward, up) - :return: A ``quat`` that points in the ``forward`` direction using ``up`` to orient it correctly + Create a rotation that points in the ``forward`` direction, oriented using ``up`` + + :param forward: The forward direction + :type forward: vec3 + :param up: The up direction used for orientation + :type up: vec3 + :return: A ``quat`` that points in the ``forward`` direction + :rtype: quat .. helptext:: create a rotation looking in a forward direction - .. lua:staticmethod:: fromToRotate(from, to) + .. lua:staticmethod:: fromToRotation(from, to) + + Create a rotation that rotates from one direction to another :param from: The direction to rotate from :type from: vec3 :param to: The direction to rotate to :type to: vec3 - :return: a ``quat`` containing a relative rotation between the ``from`` and ``to`` vectors + :return: A ``quat`` containing the relative rotation from ``from`` to ``to`` + :rtype: quat .. helptext:: create a rotation from one direction to another .. lua:staticmethod:: angleAxis(angle, axis) + Create a rotation of ``angle`` degrees around an ``axis`` + :param angle: The amount of rotation in degrees :type angle: number :param axis: The axis of rotation :type axis: vec3 - :return: a new ``quat`` containing a rotation defined by ``angle`` (in degrees) rotated about the ``axis`` vector + :return: A new ``quat`` representing the rotation + :rtype: quat .. helptext:: create a rotation from an angle and axis .. lua:staticmethod:: eulerAngles(x, y, z) - :param x: The amount of rotation about the x axis (yaw) in degrees + Create a rotation from euler angles (yaw, pitch, roll) in degrees + + :param x: The amount of rotation about the x axis (pitch) in degrees :type x: number - :param y: The amount of rotation about the y axis (pitch) in degrees + :param y: The amount of rotation about the y axis (yaw) in degrees :type y: number :param z: The amount of rotation about the z axis (roll) in degrees :type z: number - :return: a new ``quat`` containing a rotation defined by 3 euler angles (i.e. yaw, pitch roll) in radians + :return: A new ``quat`` representing the combined rotation + :rtype: quat .. helptext:: create a rotation from euler angles .. lua:attribute:: x: number - The x component of this vector + The x imaginary component .. helptext:: get or set the x component .. lua:attribute:: y: number - The y component of this vector + The y imaginary component .. helptext:: get or set the y component .. lua:attribute:: z: number - The z component of this vector + The z imaginary component .. helptext:: get or set the z component .. lua:attribute:: w: number - The w component of this vector + The scalar (real) component .. helptext:: get or set the w component .. lua:attribute:: angles: vec3 - A set of euler angles (in degrees) that generates the same rotation as this quaternion + A set of euler angles (in degrees) that produces the same rotation as this quaternion - *Please note that the potential euler angles from any given quaternion are ambiguous and should not be relied upon for smooth or consistent rotations especially when interpolating them* + *Note: euler angles derived from a quaternion are ambiguous and should not be relied upon for smooth interpolation* .. helptext:: get the euler angles of this quaternion - .. lua:method:: slerp(q, t) + .. lua:method:: slerp(q, t) -> quat + + Spherically interpolate between this quaternion and another - :param q: The other quaternion to slerp to - :param t: The amount of interpolation (from 0 to 1) - :return: a new ``quat`` that is spherically interpolated from this quaternion to ``q`` via ``t`` (between 0 and 1) + :param q: The target quaternion + :type q: quat + :param t: The interpolation amount (between 0 and 1) + :type t: number + :return: A new ``quat`` spherically interpolated from this to ``q`` by ``t`` + :rtype: quat .. helptext:: spherically interpolate this quaternion with another - .. lua:method:: conjugate() + .. lua:method:: conjugate() -> quat + + Return the conjugate of this quaternion - :return: a new ``quat`` containing the conjugate of this quaternion + :return: A new ``quat`` containing the conjugate (inverse rotation) + :rtype: quat .. helptext:: return the conjugate of this quaternion .. lua:method:: normalize() - Normalizes this quaternion + Normalize this quaternion in-place .. helptext:: normalize this quaternion - .. lua:method:: normalized() + .. lua:method:: normalized() -> quat - :return: a normalized copy of this quaternion + Return a normalized copy of this quaternion + + :return: A normalized copy of this quaternion + :rtype: quat .. helptext:: return a normalized copy of this quaternion @@ -514,9 +756,7 @@ Quaternions .. lua:class:: mat2 - A simple 2x2 matrix - - Each entry can be accessed via an index as well + A simple 2x2 matrix. Individual entries can be accessed via a 1-based index .. code-block:: lua @@ -528,38 +768,62 @@ Quaternions mat2(v1, v2) mat2(m11, m12, m21, m22) - Create a new ``mat2``, default, diagonals, 2 ``vec2`` objects or all 4 entries + Create a new ``mat2``: default (identity), diagonal scalar, two ``vec2`` columns, or all 4 entries + + :param s: Diagonal scalar value + :type s: number + :param v1: First column vector + :type v1: vec2 + :param v2: Second column vector + :type v2: vec2 .. helptext:: create a new 2x2 matrix - .. lua:method:: inverse() + .. lua:method:: inverse() -> mat2 + + Return the inverse of this matrix - :return: the inverse of this matrix + :return: The inverse of this matrix + :rtype: mat2 .. helptext:: return the inverse of this matrix - .. lua:method:: transpose() + .. lua:method:: transpose() -> mat2 + + Return the transpose of this matrix - :return: the transpose of this matrix + :return: The transpose of this matrix + :rtype: mat2 .. helptext:: return the transpose of this matrix - .. lua:method:: determinant() + .. lua:method:: determinant() -> number - :return: the determinant of this matrix + Return the determinant of this matrix + + :return: The determinant + :rtype: number .. helptext:: return the determinant of this matrix - .. lua:method:: row(index) + .. lua:method:: row(index) -> vec2 + + Return the row at a given index - :return: the row at a given ``index`` (starting at 1) + :param index: The 1-based row index + :type index: number + :return: The row at the given index :rtype: vec2 .. helptext:: return the row at the given index - .. lua:method:: column(index) + .. lua:method:: column(index) -> vec2 - :return: the column at a given ``index`` (starting at 1) + Return the column at a given index + + :param index: The 1-based column index + :type index: number + :return: The column at the given index :rtype: vec2 .. helptext:: return the column at the given index @@ -570,9 +834,7 @@ Quaternions .. lua:class:: mat3 - A simple 3x3 matrix - - Each entry can be accessed via an index as well + A simple 3x3 matrix. Individual entries can be accessed via a 1-based index .. code-block:: lua @@ -584,38 +846,64 @@ Quaternions mat3(v1, v2, v3) mat3(m11, m12, m31, ..., m33) - Create a new ``mat3``, default, diagonals, 3 ``vec3`` objects or all 9 entries + Create a new ``mat3``: default (identity), diagonal scalar, three ``vec3`` columns, or all 9 entries + + :param s: Diagonal scalar value + :type s: number + :param v1: First column vector + :type v1: vec3 + :param v2: Second column vector + :type v2: vec3 + :param v3: Third column vector + :type v3: vec3 .. helptext:: create a new 3x3 matrix - .. lua:method:: inverse() + .. lua:method:: inverse() -> mat3 - :return: the inverse of this matrix + Return the inverse of this matrix + + :return: The inverse of this matrix + :rtype: mat3 .. helptext:: return the inverse of this matrix - .. lua:method:: transpose() + .. lua:method:: transpose() -> mat3 + + Return the transpose of this matrix - :return: the transpose of this matrix + :return: The transpose of this matrix + :rtype: mat3 .. helptext:: return the transpose of this matrix - .. lua:method:: determinant() + .. lua:method:: determinant() -> number - :return: the determinant of this matrix + Return the determinant of this matrix + + :return: The determinant + :rtype: number .. helptext:: return the determinant of this matrix - .. lua:method:: row(index) + .. lua:method:: row(index) -> vec3 + + Return the row at a given index - :return: the row at a given ``index`` (starting at 1) + :param index: The 1-based row index + :type index: number + :return: The row at the given index :rtype: vec3 .. helptext:: return the row at the given index - .. lua:method:: column(index) + .. lua:method:: column(index) -> vec3 + + Return the column at a given index - :return: the column at a given ``index`` (starting at 1) + :param index: The 1-based column index + :type index: number + :return: The column at the given index :rtype: vec3 .. helptext:: return the column at the given index @@ -626,9 +914,7 @@ Quaternions .. lua:class:: mat4 - A simple 4x4 matrix, typically used for 3D homogonous transformations - - Each entry can be accessed via an index as well + A 4x4 matrix typically used for 3D homogeneous transformations. Individual entries can be accessed via a 1-based index .. code-block:: lua @@ -640,71 +926,201 @@ Quaternions mat4(v1, v2, v3, v4) mat4(m11, m12, m31, m41, ..., m44) - Create a new ``mat4``, default, diagonals, 4 ``vec4`` objects or all 16 entries + Create a new ``mat4``: default (identity), diagonal scalar, four ``vec4`` columns, or all 16 entries + + :param s: Diagonal scalar value + :type s: number + :param v1: First column vector + :type v1: vec4 + :param v2: Second column vector + :type v2: vec4 + :param v3: Third column vector + :type v3: vec4 + :param v4: Fourth column vector + :type v4: vec4 .. helptext:: create a new 4x4 matrix .. lua:staticmethod:: lookAt(eye, center, up) + Create a look-at view matrix + + :param eye: The position of the camera + :type eye: vec3 + :param center: The point to look at + :type center: vec3 + :param up: The up direction + :type up: vec3 + :return: A view matrix looking from ``eye`` toward ``center`` + :rtype: mat4 + .. helptext:: create a look-at view matrix .. lua:staticmethod:: lookAt(matrix, eye, center, up) + Apply a look-at transform to an existing matrix + + :param matrix: The matrix to apply the transform to + :type matrix: mat4 + :param eye: The position of the camera + :type eye: vec3 + :param center: The point to look at + :type center: vec3 + :param up: The up direction + :type up: vec3 + :return: The transformed matrix + :rtype: mat4 + .. helptext:: apply a look-at transform to a matrix .. lua:staticmethod:: orbit(origin, distance, x, y) + Create an orbit view matrix centered on a point + + :param origin: The point to orbit around + :type origin: vec3 + :param distance: The distance from the origin + :type distance: number + :param x: The horizontal orbit angle in degrees + :type x: number + :param y: The vertical orbit angle in degrees + :type y: number + :return: An orbit view matrix + :rtype: mat4 + .. helptext:: create an orbit view matrix .. lua:staticmethod:: orbit(matrix, origin, distance, x, y) + Apply an orbit transform to an existing matrix + + :param matrix: The matrix to apply the transform to + :type matrix: mat4 + :param origin: The point to orbit around + :type origin: vec3 + :param distance: The distance from the origin + :type distance: number + :param x: The horizontal orbit angle in degrees + :type x: number + :param y: The vertical orbit angle in degrees + :type y: number + :return: The transformed matrix + :rtype: mat4 + .. helptext:: apply an orbit transform to a matrix .. lua:staticmethod:: ortho(left, right, top, bottom, [near, far]) + Create an orthographic projection matrix + + :param left: The left clipping plane + :type left: number + :param right: The right clipping plane + :type right: number + :param top: The top clipping plane + :type top: number + :param bottom: The bottom clipping plane + :type bottom: number + :param near: The near clipping plane (optional) + :type near: number + :param far: The far clipping plane (optional) + :type far: number + :return: An orthographic projection matrix + :rtype: mat4 + .. helptext:: setup an orthographic view .. lua:staticmethod:: perspective(fovy, aspect, near, far) + Create a perspective projection matrix + + :param fovy: The vertical field of view in degrees + :type fovy: number + :param aspect: The aspect ratio (width / height) + :type aspect: number + :param near: The near clipping plane distance + :type near: number + :param far: The far clipping plane distance + :type far: number + :return: A perspective projection matrix + :rtype: mat4 + .. helptext:: setup a perspective view .. lua:staticmethod:: rotate(angle, axis) + Create a rotation matrix + + :param angle: The rotation angle in degrees + :type angle: number + :param axis: The axis of rotation + :type axis: vec3 + :return: A rotation matrix + :rtype: mat4 + .. helptext:: rotate the current transform .. lua:staticmethod:: rotate(matrix, angle, axis) + Apply a rotation transform to an existing matrix + + :param matrix: The matrix to rotate + :type matrix: mat4 + :param angle: The rotation angle in degrees + :type angle: number + :param axis: The axis of rotation + :type axis: vec3 + :return: The rotated matrix + :rtype: mat4 + .. helptext:: rotate a matrix by an angle and axis - .. lua:method:: inverse() + .. lua:method:: inverse() -> mat4 + + Return the inverse of this matrix - :return: the inverse of this matrix + :return: The inverse of this matrix + :rtype: mat4 .. helptext:: return the inverse of this matrix - .. lua:method:: transpose() + .. lua:method:: transpose() -> mat4 + + Return the transpose of this matrix - :return: the transpose of this matrix + :return: The transpose of this matrix + :rtype: mat4 .. helptext:: return the transpose of this matrix - .. lua:method:: determinant() + .. lua:method:: determinant() -> number - :return: the determinant of this matrix + Return the determinant of this matrix + + :return: The determinant + :rtype: number .. helptext:: return the determinant of this matrix - .. lua:method:: row(index) + .. lua:method:: row(index) -> vec4 - :return: the row at a given ``index`` (starting at 1) - :rtype: vec3 + Return the row at a given index + + :param index: The 1-based row index + :type index: number + :return: The row at the given index + :rtype: vec4 .. helptext:: return the row at the given index - .. lua:method:: column(index) + .. lua:method:: column(index) -> vec4 - :return: the column at a given ``index`` (starting at 1) - :rtype: vec3 + Return the column at a given index + + :param index: The 1-based column index + :type index: number + :return: The column at the given index + :rtype: vec4 .. helptext:: return the column at the given index @@ -714,3 +1130,59 @@ Axis-Aligned Bounding Box (AABB) .. lua:module:: bounds .. lua:class:: aabb + + An axis-aligned bounding box defined by minimum and maximum corner points + + :param min: The minimum corner of the bounding box + :type min: vec3 + :param max: The maximum corner of the bounding box + :type max: vec3 + + :syntax: + .. code-block:: lua + + b = bounds.aabb(vec3(-1, -1, -1), vec3(1, 1, 1)) + + .. lua:attribute:: min: vec3 + + The minimum corner of this bounding box + + .. helptext:: get or set the minimum corner + + .. lua:attribute:: max: vec3 + + The maximum corner of this bounding box + + .. helptext:: get or set the maximum corner + + .. lua:attribute:: size: vec3 + + The size (width, height, depth) of this bounding box + + .. helptext:: get the size of this bounding box + + .. lua:attribute:: center: vec3 + + The center point of this bounding box + + .. helptext:: get the center of this bounding box + + .. lua:method:: set(min, max) + + Set the minimum and maximum corners of this bounding box + + :param min: The new minimum corner + :type min: vec3 + :param max: The new maximum corner + :type max: vec3 + + .. helptext:: set the min and max corners of this bounding box + + .. lua:method:: translate(offset) + + Translate this bounding box by an offset + + :param offset: The translation offset + :type offset: vec3 + + .. helptext:: translate this bounding box by an offset From e584c958eb79d9607ca97f4b5157ff78e52e6c4a Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Sun, 26 Apr 2026 21:41:44 +0930 Subject: [PATCH 03/11] More docs improvements --- docs/source/api/math_types.rst | 20 -- docs/source/chapters_config.json | 2 +- docs/source/index.rst | 1 + docs/source/manual/vectors.rst | 349 +++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+), 21 deletions(-) create mode 100644 docs/source/manual/vectors.rst diff --git a/docs/source/api/math_types.rst b/docs/source/api/math_types.rst index 558fe4a..17bdf2d 100644 --- a/docs/source/api/math_types.rst +++ b/docs/source/api/math_types.rst @@ -587,26 +587,6 @@ Math .. helptext:: unpack this vector as multiple numbers -Vector Swizzling -################ - -``vec2``, ``vec3`` and ``vec4`` support swizzling, which allows you to access and manipulate their components in a variety of ways - -.. code-block:: lua - - v1 = vec4(1, 2, 3, 4) - v2 = vec3(5, 6, 7) - - -- Reading - print(v1.wzyx) -- prints '(4.0, 3.0, 2.0, 1.0)' - print(v1.zzz) -- prints '(3.0, 3.0, 3.0)' - print(v2.xz) -- prints '(5.0, 7.0)' - - -- Writing - v1.yx = vec2(5, 6) -- v1 is now '(6.0, 5.0, 3.0, 4.0)' - v1.xyz = v2.yzx -- v1 is now '(6.0, 7.0, 5.0, 4.0)' - - Quaternions ########### diff --git a/docs/source/chapters_config.json b/docs/source/chapters_config.json index a209506..a7a9ce9 100644 --- a/docs/source/chapters_config.json +++ b/docs/source/chapters_config.json @@ -53,7 +53,7 @@ "title": "Math & Types", "subtitle": "Vector, matrix and mathematical types", "icon": "ChapterIconVector", - "entries": ["api/math_types", "api/matrix"] + "entries": ["manual/vectors", "api/math_types", "api/matrix"] }, { "id": "Display", diff --git a/docs/source/index.rst b/docs/source/index.rst index 8dcd284..5b07696 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -36,6 +36,7 @@ Codea 4 manual/physics3d manual/scenes manual/shaders + manual/vectors .. toctree:: :maxdepth: 2 diff --git a/docs/source/manual/vectors.rst b/docs/source/manual/vectors.rst new file mode 100644 index 0000000..4f25068 --- /dev/null +++ b/docs/source/manual/vectors.rst @@ -0,0 +1,349 @@ +Using Vectors +============= + +Vectors represent positions, directions, and velocities in 2D and 3D space. This guide walks through the most common vector patterns in game development, each with a complete runnable example. + +Moving Along a Vector +##################### + +The most fundamental vector operation in games is movement: add a velocity vector to a position each frame, and an object moves. Because velocity has both **direction** and **magnitude** (speed), you can fire a bullet toward any point on screen with just a subtraction, a normalization, and a scale. + +**Shooting bullets toward a touch** + +.. code-block:: lua + + function setup() + bullets = {} + origin = vec2(WIDTH/2, HEIGHT/2) + end + + function draw() + background(20, 25, 35) + + -- Move and draw each bullet + for i = #bullets, 1, -1 do + local b = bullets[i] + b.pos = b.pos + b.vel * DeltaTime + + fill(255, 220, 50) + ellipse(b.pos.x, b.pos.y, 8, 8) + + -- Remove once off-screen + if b.pos.x < 0 or b.pos.x > WIDTH or + b.pos.y < 0 or b.pos.y > HEIGHT then + table.remove(bullets, i) + end + end + + -- Draw turret + fill(100, 180, 255) + ellipse(origin.x, origin.y, 28, 28) + end + + function touched(touch) + if touch.state == BEGAN then + local dir = (vec2(touch.x, touch.y) - origin):normalized() + table.insert(bullets, { + pos = vec2(origin.x, origin.y), + vel = dir * 600 -- 600 pixels per second + }) + end + end + +Subtracting two positions gives a direction vector. ``:normalized()`` scales it to length 1 so that multiplying by ``600`` always produces the same speed, no matter how far away the touch was. + +Rotating Vectors +################ + +``:rotate(angleRadians)`` rotates a ``vec2`` by an angle. Rotating an offset vector each frame and adding it to a center point produces natural circular motion — no trigonometry functions needed. + +**Objects orbiting a center point** + +.. code-block:: lua + + function setup() + center = vec2(WIDTH/2, HEIGHT/2) + moons = { + { offset = vec2(140, 0), speed = 1.2, size = 18, r = 255, g = 100, b = 100 }, + { offset = vec2(220, 0), speed = 0.7, size = 24, r = 100, g = 180, b = 255 }, + { offset = vec2( 80, 0), speed = 2.4, size = 12, r = 120, g = 255, b = 140 }, + } + end + + function draw() + background(10, 12, 20) + + -- Central body + fill(255, 200, 80) + ellipse(center.x, center.y, 40, 40) + + for _, moon in ipairs(moons) do + moon.offset = moon.offset:rotate(moon.speed * DeltaTime) + local pos = center + moon.offset + + -- Draw orbit ring + stroke(60, 65, 80) + strokeWidth(1) + noFill() + ellipse(center.x, center.y, moon.offset:length() * 2, moon.offset:length() * 2) + + -- Draw moon + noStroke() + fill(moon.r, moon.g, moon.b) + ellipse(pos.x, pos.y, moon.size, moon.size) + end + end + +Each moon stores only its offset from the center. Rotating that offset by ``speed * DeltaTime`` radians per frame moves it around the orbit automatically. + +Dot Product — Field of View +########################### + +The dot product of two **normalized** vectors equals ``cos(θ)`` where ``θ`` is the angle between them. This makes it a fast way to ask *"is this target within my field of view?"* without computing any angles. + +- ``dot == 1`` → same direction (0°) +- ``dot == 0`` → perpendicular (90°) +- ``dot == -1`` → opposite (180°) + +If you want a 120° cone (60° either side of forward), pre-compute ``math.cos(math.pi/3)`` and compare the dot product against it. + +**Guard with a cone of vision** + +.. code-block:: lua + + function setup() + guardPos = vec2(WIDTH/2, HEIGHT/2) + guardDir = vec2(0, 1) -- facing up + fovCos = math.cos(math.pi / 3) -- 60° half-angle → 120° total FOV + sightDist = 220 + end + + function draw() + background(25, 30, 40) + + -- Guard slowly rotates + guardDir = guardDir:rotate(0.6 * DeltaTime) + + local touchPos = vec2(CurrentTouch.x, CurrentTouch.y) + local toTouch = touchPos - guardPos + local dist = toTouch:length() + + -- Dot product test: within range AND within cone? + local spotted = dist < sightDist and dist > 1 and + guardDir:dot(toTouch / dist) >= fovCos + + -- Draw sight cone (two edge lines) + local left = guardDir:rotate( math.pi/3) * sightDist + local right = guardDir:rotate(-math.pi/3) * sightDist + stroke(spotted and color(255, 80, 80, 120) or color(80, 200, 80, 80)) + strokeWidth(1) + fill(spotted and color(255, 80, 80, 30) or color(80, 200, 80, 20)) + -- draw the cone as two lines from guard + line(guardPos.x, guardPos.y, guardPos.x + left.x, guardPos.y + left.y) + line(guardPos.x, guardPos.y, guardPos.x + right.x, guardPos.y + right.y) + + -- Draw guard + noStroke() + fill(150, 190, 255) + ellipse(guardPos.x, guardPos.y, 28, 28) + + -- Draw touch target + fill(spotted and color(255, 80, 80) or color(80, 255, 80)) + ellipse(touchPos.x, touchPos.y, 20, 20) + + fill(255) + fontSize(20) + text(spotted and "SPOTTED!" or "hidden", WIDTH/2, 50) + end + +Move your finger around the screen. The guard rotates slowly — watch for the moment the dot product crosses the threshold and the target is detected. + +Smooth Following with Lerp +########################## + +``:lerp(target, t)`` blends between two vectors. Calling it every frame with a small ``t`` (proportional to ``DeltaTime``) makes an object ease toward its target — the further away it is, the faster it moves, gradually slowing as it closes in. + +This pattern appears everywhere: cameras, enemy AI homing, UI elements sliding into place, and health bars draining smoothly. + +**Camera that lags behind the player** + +.. code-block:: lua + + function setup() + playerPos = vec2(WIDTH/2, HEIGHT/2) + cameraPos = vec2(WIDTH/2, HEIGHT/2) + end + + function draw() + -- Player snaps to touch + if CurrentTouch.state ~= ENDED then + playerPos = playerPos:lerp( + vec2(CurrentTouch.x, CurrentTouch.y), 12 * DeltaTime) + end + + -- Camera lazily follows + cameraPos = cameraPos:lerp(playerPos, 4 * DeltaTime) + + background(20, 28, 38) + + -- World grid, offset by camera + local offset = vec2(WIDTH/2, HEIGHT/2) - cameraPos + stroke(45, 55, 70) + strokeWidth(1) + for gx = -8, 8 do + for gy = -8, 8 do + local wx = gx * 70 + offset.x + local wy = gy * 70 + offset.y + line(wx - 6, wy, wx + 6, wy) + line(wx, wy - 6, wx, wy + 6) + end + end + + -- Player is always drawn at the screen center (the camera follows them) + noStroke() + fill(100, 220, 110) + ellipse(WIDTH/2, HEIGHT/2, 26, 26) + end + +The factor ``4 * DeltaTime`` controls lag — increase it to snap the camera closer, decrease it for more cinematic drift. The player uses ``12 * DeltaTime`` to stay responsive. + +Reflecting Vectors — Bouncing Ball +################################### + +``:reflect(normal)`` flips a direction vector about a surface normal. It is the correct and efficient way to handle elastic collisions with flat surfaces. + +The normal always points *away from* the surface: ``vec2(1, 0)`` for a left wall, ``vec2(0, 1)`` for a floor, and so on. + +**Ball bouncing around the screen** + +.. code-block:: lua + + function setup() + pos = vec2(WIDTH/2, HEIGHT/2) + vel = vec2(280, 350) + radius = 22 + end + + function draw() + background(18, 22, 32) + + pos = pos + vel * DeltaTime + + -- Bounce off left/right + if pos.x - radius < 0 then + pos.x = radius + vel = vel:reflect(vec2(1, 0)) + elseif pos.x + radius > WIDTH then + pos.x = WIDTH - radius + vel = vel:reflect(vec2(-1, 0)) + end + + -- Bounce off bottom/top + if pos.y - radius < 0 then + pos.y = radius + vel = vel:reflect(vec2(0, 1)) + elseif pos.y + radius > HEIGHT then + pos.y = HEIGHT - radius + vel = vel:reflect(vec2(0, -1)) + end + + -- Draw shadow + fill(0, 0, 0, 60) + ellipse(pos.x + 6, pos.y - 6, radius * 2, radius * 2) + + -- Draw ball + fill(255, 100, 60) + ellipse(pos.x, pos.y, radius * 2, radius * 2) + end + +The same pattern works for angled surfaces — just use the surface's perpendicular as the normal. A 45° ramp has normal ``vec2(1, 1):normalized()``. + +Proximity Detection +################### + +``:distance()`` measures the straight-line gap between two positions. Comparing it against a threshold radius is the simplest possible collision or pickup test. + +**Collecting coins** + +.. code-block:: lua + + function setup() + playerPos = vec2(WIDTH/2, HEIGHT/2) + coins = {} + for i = 1, 12 do + table.insert(coins, vec2( + math.random(60, WIDTH - 60), + math.random(60, HEIGHT - 60))) + end + score = 0 + end + + function draw() + background(22, 28, 38) + + -- Player follows touch + if CurrentTouch.state ~= ENDED then + playerPos = playerPos:lerp( + vec2(CurrentTouch.x, CurrentTouch.y), 10 * DeltaTime) + end + + -- Collect any coin within range + for i = #coins, 1, -1 do + if playerPos:distance(coins[i]) < 38 then + table.remove(coins, i) + score = score + 1 + end + end + + -- Draw coins + fill(255, 200, 40) + for _, c in ipairs(coins) do + ellipse(c.x, c.y, 22, 22) + end + + -- Draw player + fill(80, 160, 255) + ellipse(playerPos.x, playerPos.y, 30, 30) + + fill(255) + fontSize(22) + text("Score: " .. score, WIDTH/2, HEIGHT - 40) + + if #coins == 0 then + fontSize(36) + text("All collected!", WIDTH/2, HEIGHT/2) + end + end + +For performance with many objects, compare ``:distance2()`` against the squared radius instead — it skips the square root entirely. + +Vector Swizzling +################ + +``vec2``, ``vec3`` and ``vec4`` support swizzling, which lets you read or write multiple components in any order using ``xyzw`` (or equivalently ``rgba``) notation. + +.. code-block:: lua + + v1 = vec4(1, 2, 3, 4) + v2 = vec3(5, 6, 7) + + -- Reading — any combination of components + print(v1.wzyx) -- prints '(4.0, 3.0, 2.0, 1.0)' + print(v1.zzz) -- prints '(3.0, 3.0, 3.0)' + print(v2.xz) -- prints '(5.0, 7.0)' + + -- Writing — assign to a subset of components at once + v1.yx = vec2(5, 6) -- v1 is now '(6.0, 5.0, 3.0, 4.0)' + v1.xyz = v2.yzx -- v1 is now '(6.0, 7.0, 5.0, 4.0)' + +Swizzling is particularly useful when working with shader code or converting between ``vec3`` positions and ``vec4`` homogeneous coordinates: + +.. code-block:: lua + + local pos3 = vec3(1, 2, 3) + + -- Lift to homogeneous coords (w = 1 for a position) + local pos4 = vec4(pos3.x, pos3.y, pos3.z, 1) + + -- Extract just the XZ plane (useful for flat-ground games) + local flat = pos3.xz -- returns vec2(1, 3) From 6c97e5b50cbf86d2fca7f0a905ae0bcf6b054e09 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Tue, 28 Apr 2026 10:33:39 +0930 Subject: [PATCH 04/11] More docs changes --- README.md | 129 ++ docs/source/api/graphics.rst | 4 + docs/source/api/lua.rst | 1664 ++++++++++++++++++++++++- docs/source/api/pasteboard.rst | 4 +- docs/source/api/require.rst | 1 + docs/source/api/sound.rst | 4 + docs/source/api/style.rst | 43 + docs/source/builders/luadoc.py | 41 +- docs/source/builders/luastruct.py | 52 +- docs/source/conf.py | 5 +- docs/source/extensions/codeasymbol.py | 36 + docs/source/extensions/editor.py | 29 + 12 files changed, 1964 insertions(+), 48 deletions(-) create mode 100644 docs/source/extensions/codeasymbol.py create mode 100644 docs/source/extensions/editor.py diff --git a/README.md b/README.md index e986c2a..e9551eb 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,135 @@ make html The output lands in `docs/build/html/`. +## API Authoring Requirements + +The API `.rst` files are used by Codea as structured editor metadata, not only as +rendered documentation. The `luadoc` build emits JSON that Codea consumes for +Reference, autocomplete, syntax highlighting, and editor affordances. + +### Module Namespacing + +Use Sphinx's Lua module context as the source of truth for namespacing. + +```rst +.. lua:module:: pasteboard + +.. lua:attribute:: name: string +``` + +This emits `name` with `module: "pasteboard"`, so Codea treats the symbol as +`pasteboard.name`. Reference UI should display the qualified name for module +members. + +Rules: + +- Use `.. lua:module:: name` once when introducing and documenting a module. +- Use `.. lua:currentmodule:: name` to return to an existing module context + without creating another module entry. +- Use `.. lua:currentmodule:: None` before globals in a file that previously set + a module context. +- Do not rely on section headings to imply namespacing. The current Lua module + context is the authoritative signal. + +For mixed files, be explicit: + +```rst +.. lua:module:: style + +.. lua:function:: fill() + +Constants +********* + +.. lua:currentmodule:: None + +.. lua:attribute:: LEFT: const + +.. lua:currentmodule:: style + +.. lua:function:: textAlign(align) +``` + +Here `style.fill` is namespaced, `LEFT` is global, and later functions return to +the `style` namespace. + +### Symbol Annotations + +Use `.. symbol::` for syntax-highlighting classifications. Symbol annotations +are inherited by descendant API entries until a more-specific `.. symbol::` +replaces them. + +Supported symbol types: + +- `api-call`: Codea API call highlighting. This is the default for docs-derived + functions when no symbol metadata is present. +- `lua-api`: Lua standard library highlighting. +- `const`: Constant highlighting. This maps to Codea's existing constant symbol + type. + +Examples: + +```rst +.. symbol:: lua-api + +.. lua:function:: print(...) + +.. lua:attribute:: pi: const + + .. symbol:: lua-api const +``` + +```rst +.. lua:attribute:: STANDARD: const + + .. symbol:: const + :group: viewer-mode +``` + +The optional `:group:` value is emitted as `symbol.group`. Codea can use this to +identify replaceable symbol sets, such as viewer modes, text alignment values, +bit masks, or other related constants. + +Use symbol annotations for semantic coloring and symbol-map behavior. Do not use +them for popover/editor UI features. + +### Editor Annotations + +Use `.. editor::` for editor affordances only. These roles are emitted as editor +metadata and mapped by Codea to existing `LuaSymbolType` affordance flags. + +Supported roles include: + +- `color`: color picker affordance +- `sprite`: sprite/image asset affordance +- `text`: text/font-related affordance +- `import`: asset import affordance, for APIs such as `require` +- `sound`: sound asset affordance +- `music`: music asset affordance +- `font`: font picker affordance +- `shader`: shader affordance +- `model`: model asset affordance + +Example: + +```rst +.. lua:function:: fill() + + Sets the fill color. + + .. editor:: color +``` + +This lets Codea derive `style.fill` as both an API call and a color API call, so +the editor can apply the correct highlighting and show the color interaction. + +Keep editor roles separate from symbol classifications: + +- Use `.. symbol:: lua-api` for Lua API coloring. +- Use `.. symbol:: const` for constant coloring. +- Use `.. editor:: color`, `sprite`, `import`, etc. only when tapping or editing + the symbol should expose a special editor interaction. + ## Scripts ### `scripts/check_helptexts.py` diff --git a/docs/source/api/graphics.rst b/docs/source/api/graphics.rst index 9b0708a..dce32b3 100644 --- a/docs/source/api/graphics.rst +++ b/docs/source/api/graphics.rst @@ -11,6 +11,7 @@ Background Clears the current context with solid color, can also be used to set image backgrounds when combined with :lua:func:`context.push` .. helptext:: set the background color, image or shader + .. editor:: color .. lua:function:: background(cubeImage, [mipLevel = 0]) @@ -146,11 +147,13 @@ Sprites Draws a sprite using an asset - :lua:class:`image`, :lua:class:`asset.key` or :lua:class:`sprite.slice` .. helptext:: draw a sprite or image + .. editor:: sprite .. lua:function:: sprite(shader, x, y, w, h) .. helptext:: draw using a shader + .. editor:: shader Text @@ -161,6 +164,7 @@ Text Draws one or more lines of text based on the current style. Use the optional width and height parameters to draw a fixed size text box with line wrapping enabled .. helptext:: draw text at a location + .. editor:: text - *Text Color* with :lua:func:`style.fill` - *Text Outline* with :lua:func:`style.stroke` diff --git a/docs/source/api/lua.rst b/docs/source/api/lua.rst index 224e15b..41d8914 100644 --- a/docs/source/api/lua.rst +++ b/docs/source/api/lua.rst @@ -1,53 +1,1653 @@ lua === -**Global Functions** +.. symbol:: lua-api -.. lua:function:: typeof(o) +Overview +-------- - Returns a user-friendly string representation of the type of the object. +For Loops Overview +~~~~~~~~~~~~~~~~~~ - .. helptext:: get the type name of an object +You can use **for loops** in Lua to iterate over arrays and tables, performing tasks for each element. - :param o: The object to check - :type o: any - :return: The type of the object +This example simply iterates i over the values 1 to 10 + +.. code-block:: lua + + -- Iterate from 1 to 10 + for i = 1, 10 do + print( i ) + end + + +This example uses the `ipairs` function to sequentially iterate over a table. Note that ipairs only works on tables that have sequential elements beginning at 1. + +.. code-block:: lua + + -- Iterate over an array named 'arr' + arr = { 3, 2, 1 } + + for i,v in ipairs(arr) do + print( "arr["..i.."] = "..v ) + end + + -- Prints: + -- arr[1] = 3 + -- arr[2] = 2 + -- arr[3] = 1 + + +This example uses `pairs` to iterate over all the key/value pairs in a table, unlike `ipairs` the keys do not have to be integral values, and can be anything. + +.. code-block:: lua + + -- Iterate over a table named 'tab' + tab = { x = 3, y = "string", z = 1 } + + for key,value in pairs(tab) do + print( "tab."..key.." = "..value ) + end + + -- Prints: + -- tab.y = string + -- tab.x = 3 + -- tab.z = 1 + +Conditionals Overview +~~~~~~~~~~~~~~~~~~~~~ + +Use **conditionals** to test for a particular circumstance and then branch your code appropriately. See the examples below. + +.. code-block:: lua + + -- Check if x is equal to 10 + if x == 10 then + print( "x equals 10" ) + end + + -- else and elseif + if x == 10 then + print( "x is 10" ) + elseif x == 5 then + print( "x is 5" ) + else + print( "x is something else: "..x ) + end + + -- Checking multiple values + if x == 10 and y < 5 then + print( "x is 10 and y is less than 5" ) + elseif x == 5 or y == 3 then + print( "x is 5 or y is 3" ) + else + print( "x is "..x.." and y is "..y ) + end + +While Loops Overview +~~~~~~~~~~~~~~~~~~~~ + +Use while and repeat loops to repeat code until a condition is true. While loops are used when you can't predict the number of times a loop will iterate, such as when reading input from a file or testing for a key press. + +.. code-block:: lua + + -- simple while loop. The condition is evaluated at the beginning of the loop. The loop will never get executed if the condition is false at the beginning of the loop. + while i<=5 do + print(i) + i=i+1 + end + + -- repeat loop. The condition is evaluated at the end of the loop, so the code always gets executed at least once + repeat + print(i) + i=i+1 + until i>5 + + -- break statement: break exits a loop and continues program flow with the statement immediately following the end or until condition + while true do + print(i) + i=i+1 + if i>5 then + break + end + end + +Annotations Overview +~~~~~~~~~~~~~~~~~~~~ + +Annotations can be used to indicate the type of a variable to Codea, giving you better auto-completion support. + +To write a code annotation, start a comment with three dashes (`---`). Codea will look at the next line of code to try and auto-complete your comment if it is an assignement or function definition. You can also manually write annotations anywhere in your code following the format `--- variableName: variableType`. + +When using annotations for Objective-C types, this will add Objective-C properties and methods auto-completion to your variables. + +.. code-block:: lua + + -- method annotation + --- makeBox Creates a box body + --- pos: vec2, bottom-left position of the box + --- size: vec2, size of the box + function createBoxBody(pos, size) + + -- variable annotation + --- textView: objc.UITextView + textView = objc.UITextView() + +Language +-------- + +.. lua:function:: print( ... ) + + Prints values to the output console. + + :param ...: values to print + :syntax: + + .. code-block:: lua + + print( ... ) + +.. lua:function:: ipairs( table ) + + `ipairs` can be used to iterate over a table sequentially, starting from the index 1 and continuing until the first integer key absent from the table. Returns three values: an iterator function, the table, and 0. + + :param table: table to iterate over + :return: Returns three values: an iterator function, the table, and 0 + :rtype: table + :syntax: + + .. code-block:: lua + + for i,v in ipairs( t ) do body end + + -- This will iterate over the pairs: + -- (1,t[1]), (2,t[2]), ... + +.. lua:function:: pairs( table ) + + `pairs` can be used to iterate over a tables key-value pairs. Returns three values: the next function, the table t, and nil. + + :param table: table to iterate over + :return: Returns three values: the next function, the table t, and nil + :rtype: table + :syntax: + + .. code-block:: lua + + for k,v in pairs( t ) do body end + + -- This will iterate over all key-value + -- pairs in table t + +Tables +------ + +.. lua:function:: table.concat( table, sep ) + + Given an array where all elements are strings or numbers, returns `table[i]..sep..table[i+1] ... sep..table[j]`. The default value for `sep` is the empty string, the default for `i` is 1, and the default for `j` is the length of the table. If `i` is greater than `j`, returns the empty string. + + :param table: table to concatenate + :param sep: separator string + :param i: int, starting index to concatenate from + :param j: int, ending index + :return: A string of the elements in `table` concatenated with each other, separated by `sep` + :rtype: string + :syntax: + + .. code-block:: lua + + table.concat( table ) + table.concat( table, sep ) + table.concat( table, sep, i ) + table.concat( table, sep, i, j ) + +.. lua:function:: table.move( a1, f, e, t, a2 ) + + Moves elements from table `a1` into table `a2`. This function performs the equivalent to the multiple assignment: a2[t], ... = a1[f], ..., a1[e]. The default value for `a2` is `a1`. The destination range can overlap with the source range. Index `f` must be positive. + + :param a1: table to move elements from + :param f: starting index in table to move from + :type f: integer + :param e: ending index in table to move from + :type e: integer + :param t: starting index in table to move into + :type t: integer + :param a2: table to move elements into (defaults to `a1`) + :syntax: + + .. code-block:: lua + + table.move( a1, f, e, t ) + table.move( a1, f, e, t, a2 ) + +.. lua:function:: table.insert( table, pos, value ) + + Inserts element `value` at position `pos` in `table`, shifting up other elements to open space, if necessary. The default value for `pos` is n+1, where n is the length of the table, so that a call `table.insert(t,x)` inserts x at the end of table t. + + :param table: table to insert into + :param pos: int, position to inset + :param value: value to insert + :syntax: + + .. code-block:: lua + + table.insert( table, value ) + table.insert( table, pos, value ) + +.. lua:function:: table.remove( table, pos ) + + Removes from `table` the element at position `pos`, shifting down other elements to close the space, if necessary. Returns the value of the removed element. The default value for `pos` is n, where n is the length of the table, so that a call `table.remove(t)` removes the last element of table t. + + :param table: table to insert into + :param pos: int, position of value to remove + :return: Value of the removed element + :syntax: + + .. code-block:: lua + + table.remove( table ) + table.remove( table, pos ) + +.. lua:function:: table.pack(...) + + Returns a new table with all parameters stored into keys 1, 2, etc. and with a field "n" with the total number of parameters. Note that the resulting table may not be a sequence. + + :param ...: arguments to pack into table + :return: table with all parameters packed + :rtype: table + :syntax: + + .. code-block:: lua + + table.pack( ... ) + +.. lua:function:: table.unpack(list) + + Returns the elements from the given list. + + :param list: list to unpack + :return: elements unpacked from list + :syntax: + + .. code-block:: lua + + table.unpack( list ) + +.. lua:function:: table.sort( table ) + + Sorts table elements in a given order, in-place, from `table[1]` to `table[n]`, where n is the length of the table. If `comp` is given, then it must be a function that receives two table elements and returns true when the first is less than the second (so that `not comp(a[i+1],a[i]`) will be true after the sort). If `comp` is not given, then the standard Lua operator < is used instead. + + The sort algorithm is not stable; that is, elements considered equal by the given order may have their relative positions changed by the sort. + + :param table: the table to sort + :param comp: a function that receives two table elements and returns true when the first is less than the second + :syntax: + + .. code-block:: lua + + table.sort( table ) + table.sort( table, comp ) + +Strings +------- + +.. lua:function:: string.byte( s, i, j ) + + Returns the internal numerical codes of the characters s[i], s[i+1], ..., s[j]. The default value for `i` is 1; the default value for `j` is `i`. These indices are corrected following the same rules of `string.sub`. + + :param s: string to use + :param i: first index, defaults to 1 + :param j: last index, defaults to `i` + :return: The internal numerical codes for characters s[i] up to s[j] + :syntax: + + .. code-block:: lua + + string.byte( s ) + string.byte( s, i ) + string.byte( s, i, j ) + +.. lua:function:: string.char( ... ) + + Returns a string with length equal to the number of arguments, in which each character has the internal numerical code equal to its corresponding argument. + + :param ...: a variable number of numerical codes + :return: String composed of characters equal to the numerical codes used as arguments + :rtype: string + :syntax: + + .. code-block:: lua + + string.char( ... ) + +.. lua:function:: string.dump( function ) + + Returns a string containing a binary representation (a *binary chunk*) of the given function, so that a later `load` on this string returns a copy of the function (with new upvalues). If `strip` is a true value, the binary representation is created without debug information about the function (local variable names, lines, etc). + + :param function: a function to convert to binary string + :param strip: whether to strip debug information + :type strip: boolean + :return: Binary string representation of the specified function + :rtype: string + :syntax: + + .. code-block:: lua + + string.dump( function ) + string.dump( function, strip ) + +.. lua:function:: string.find( s, pattern ) + + Looks for the first match of `pattern` in the string `s`. If it finds a match, then `string.find()` returns the indices of `s` where the occurrence starts and ends; otherwise, it returns `nil`. A third, optional numerical argument `init` specifies where to start the search, its default value is 1 and can be negative. A value of `true` as the fourth optional argument `plain` turns off the pattern matching facilities so the function performs a plain "find substring" operation, with no characters in `pattern` being considered "magic." Note that if `plain` is given, then `init` must be given as well. + + If the pattern has captures, then in a successful match the captured values are also returned, after the two indices. + + :param s: string to search in + :param pattern: pattern to look for + :param init: starting character in string to search from, default is 1 + :param plain: perform a plain substring search + :type plain: boolean + :return: The start and end indices of the match, or `nil` if no match. If the pattern has captures then captured values are also returned after the indices + :syntax: + + .. code-block:: lua + + string.find( s, pattern ) + string.find( s, pattern, init ) + string.find( s, pattern, init, plain ) + +.. lua:function:: string.format( formatstring, ... ) + + Returns a formatted version of its variable arguments following the description given in the first argument, which must be a string. The format string follows the same rules as the `printf` family of standard C functions. The only difference are that the options/modifiers `*, 1, L, n, p` and `h` are not supported and that there is an extra option `q`. The `q` option formats a string in a form suitable to be safely read back by the Lua interpreter, for example all double quotes, newlines, embedded zeros and backslashes will be escaped when written. + + :param formatstring: string defining the format + :return: A formatted string + :rtype: string + :syntax: + + .. code-block:: lua + + string.format( formatstring, ... ) + +.. lua:function:: string.len( s ) + + Receives a string and returns its length. The empty string "" has length 0. + + :param s: get the length of this string + :return: Length of string `s` + :rtype: string + :syntax: + + .. code-block:: lua + + string.len( s ) + +.. lua:function:: string.gmatch( s, pattern ) + + Returns an iterator function that, each time it is called, returns the next captures from pattern (see **Patterns Overview**) over the string s. If pattern specifies no captures, then the whole match is produced in each call. + + :param s: string to search + :param pattern: pattern to match + :return: Iterator function over matches of `pattern` within the string + :rtype: string + :syntax: + + .. code-block:: lua + + string.gmatch( s, pattern ) + +.. lua:function:: string.gsub( s, pattern, repl ) + + Returns a copy of `s` in which all (or the first `n`, if given) occurrences of the `pattern` (see **Patterns Overview**) have been replaced by a replacement string specified by `repl`, which can be a string, a table, or a function. gsub also returns, as its second value, the total number of matches that occurred. The name gsub comes from *Global SUBstitution*. + + If `repl` is a string, then its value is used for replacement. The character `%` works as an escape character: any sequence in `repl` of the form `%d`, with `d` between 1 and 9, stands for the value of the `d`-th captured substring. The sequence `%0` stands for the whole match. The sequence `%%` stands for a single `%`. + + If `repl` is a table, then the table is queried for every match, using the first capture as the key. + + If `repl` is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order. + + In any case, if the pattern specifies no captures, then it behaves as if the whole pattern was inside a capture. + + If the value returned by the table query or by the function call is a string or a number, then it is used as the replacement string; otherwise, if it is false or nil, then there is no replacement (that is, the original match is kept in the string). + + :param s: string to substitute + :param pattern: pattern to match + :param repl: table or function, replacement parameter + :type repl: string + :param n: number of occurrences to match (all if not specified) + :type n: integer + :return: Returns a copy of `s` with substitutions made, as well as the number of matches + :rtype: number + :syntax: + + .. code-block:: lua + + string.gsub( s, pattern, repl ) + string.gsub( s, pattern, repl, n ) + +.. lua:function:: string.lower( s ) + + Receives a string and returns a copy of this string with all uppercase letters changed to lowercase. All other characters are left unchanged. + + :param s: get a lowercase version of this string + :return: Lowercase version of string `s` + :rtype: string + :syntax: + + .. code-block:: lua + + string.lower( s ) + +.. lua:function:: string.upper( s ) + + Receives a string and returns a copy of this string with all lowercase letters changed to uppercase. All other characters are left unchanged. + + :param s: get an uppercase version of this string + :return: Uppercase version of string `s` + :rtype: string + :syntax: + + .. code-block:: lua + + string.upper( s ) + +.. lua:function:: string.match( s, pattern ) + + Looks for the first match of `pattern` in the string `s`. If it finds one then `string.match()` returns the captures from the pattern, otherwise it returns `nil`. If `pattern` specifies no captures, then the whole match is returned. A third optional numerical argument `init` specifies where to start the search. Its default is 1 and can be negative. + + :param s: string to search + :param pattern: pattern to match + :param init: starting location in string + :return: Captures from the first match of `pattern` in string `s`, or `nil` if none were found + :rtype: string + :syntax: + + .. code-block:: lua + + string.match( s, pattern ) + string.match( s, pattern, init ) + +.. lua:function:: string.rep( s, n ) + + Returns a string that is the concatenation of `n` copies of the string `s`. + + :param s: string to replicate + :param n: int, number of times to replicate the string + :return: `n` concatenations of string `s` + :rtype: string + :syntax: + + .. code-block:: lua + + string.rep( s, n ) + +.. lua:function:: string.reverse( s ) + + This function returns the string `s` reversed. + + :param s: string to reverse + :return: `s` reversed + :syntax: + + .. code-block:: lua + + string.reverse( s ) + +.. lua:function:: string.sub( s, i, j ) + + Returns the substring of `s` that starts at `i` and continues until `j`; `i` and `j` can be negative. If `j` is absent, then it is assumed to be equal to -1 (which is the same as the string length). In particular, the call `string.sub(s,1,j)` returns a prefix of `s` with length `j`, and string.sub(s, -i) returns a suffix of `s` with length `i`. + + :param s: find substring of this string + :param i: int, starting index + :param j: int, ending index + :return: Substring of string `s` + :rtype: string + :syntax: + + .. code-block:: lua + + string.sub( s, i ) + string.sub( s, i, j ) + +.. lua:function:: string.pack( fmt, v1, v2, ... ) + + Returns a binary string containing the values `v1`, `v2`, etc. packed (that is, serialized in binary form) according to the format string `fmt`. + + :param fmt: format string specifying binary format + :param ...: arguments to pack + :return: Values packed into binary string + :rtype: string + :syntax: + + .. code-block:: lua + + string.pack( fmt, v1, v2, ... ) + +.. lua:function:: string.packsize( fmt ) + + Returns the size of a string resulting from `string.pack` with the given format. The format string cannot have the variable-length options 's' or 'z' + + :param fmt: format string specifying binary format + :return: Size of string packed with the given format + :rtype: string + :syntax: + + .. code-block:: lua + + string.packsize( fmt ) + +.. lua:function:: string.unpack( fmt, s ) + + Returns the values packed in string `s` according to the format string `fmt`. An optional pos marks where to start reading in `s` (default is 1). After the read values, this function also returns the index of the first unread byte in `s`. + + :param fmt: format string specifying binary format + :param s: string to unpack + :return: The values packed in `s` + :syntax: + + .. code-block:: lua + + string.unpack( fmt, s ) + string.unpack( fmt, s, pos ) + +Patterns Overview +~~~~~~~~~~~~~~~~~ + +Patterns in Lua are described by regular strings, which are interpreted as patterns by the +pattern-matching functions `string.find`, `string.gmatch`, `string.gsub`, and `string.match`. +This section describes the syntax and the meaning (that is, what they match) of these strings. + + +**Character Class** + + +A character class is used to represent a set of characters. The following combinations are +allowed in describing a character class: + + +.. code-block:: lua + + x: (where x is not one of the magic characters + ^$()%.[]*+-?) represents the character x + itself. + .: (a dot) represents all characters. + %a: represents all letters. + %c: represents all control characters. + %d: represents all digits. + %g: represents all printable characters except space. + %l: represents all lowercase letters. + %p: represents all punctuation characters. + %s: represents all space characters. + %u: represents all uppercase letters. + %w: represents all alphanumeric characters. + %x: represents all hexadecimal digits. + %x: (where x is any non-alphanumeric character) + represents the character x. + This is the standard way to escape + the magic characters. Any non-alphanumeric + character (including all punctuations, even + the non-magical) can be preceded by a '%' + when used to represent itself in a pattern. + [set]: represents the class which is + the union of all characters in set. + A range of characters can be specified + by separating the end characters of the + range, in ascending order, with a '-'. + All classes %x described above can + also be used as components in set. + All other characters in set represent + themselves. For example, [%w_] (or [_%w]) + represents all alphanumeric characters + plus the underscore, [0-7] represents + the octal digits, and [0-7%l%-] represents + the octal digits plus the lowercase letters + plus the '-' character. + [^set]: represents the complement of set, + where set is interpreted as above. + + + +For all classes represented by single letters (%a, %c, etc.), the corresponding uppercase +letter represents the complement of the class. For instance, %S represents all non-space characters. + + +The definitions of letter, space, and other character groups depend on the current locale. +In particular, the class [a-z] may not be equivalent to %l. + + +**Pattern Item** + + +A pattern item can be: + + +.. code-block:: lua + + - a single character class, which matches + any single character in the class; + - a single character class followed by '*', + which matches zero or more repetitions + of characters in the class. These + repetition items will always match + the longest possible sequence; + - a single character class followed by '+', + which matches one or more repetitions of + characters in the class. These repetition + items will always match the longest + possible sequence; + - a single character class followed by '-', + which also matches zero or more repetitions + of characters in the class. Unlike '*', + these repetition items will always match + the shortest possible sequence; + - a single character class followed by '?', + which matches zero or one occurrence of + a character in the class. It always + matches one occurrence if possible; + - %n, for n between 1 and 9; such item + matches a substring equal to the n-th + captured string (see below); + - %bxy, where x and y are two distinct + characters; such item matches strings + that start with x, end with y, + and where the x and y are balanced. + This means that, if one reads the string + from left to right, counting +1 for an x + and -1 for a y, the ending y is the first + y where the count reaches 0. + For instance, the item %b() matches + expressions with balanced parentheses. + - %f[set], a frontier pattern; such item + matches an empty string at any position + such that the next character belongs to + set and the previous character does not + belong to set. The set set is interpreted + as previously described. The beginning + and the end of the subject are handled + as if they were the character '\0'. + + + +**Pattern** + + +A pattern is a sequence of pattern items. A caret '^' at the beginning of a pattern anchors the match at the +beginning of the subject string. A '$' at the end of a pattern anchors the match at the end of the subject string. +At other positions, '^' and '$' have no special meaning and represent themselves. + + +**Captures** + + +A pattern can contain sub-patterns enclosed in parentheses; they describe captures. When a match succeeds, +the substrings of the subject string that match captures are stored (captured) for future use. +Captures are numbered according to their left parentheses. For instance, in the pattern "(a*(.)%w(%s*))", +the part of the string matching "a*(.)%w(%s*)" is stored as the first capture +(and therefore has number 1); the character matching "." is captured with number 2, +and the part matching "%s*" has number 3. + + +As a special case, the empty capture () captures the current string position (a number). +For instance, if we apply the pattern "()aa()" on the string "flaaap", there will be two captures: 3 and 5. + +Math +---- + +.. lua:function:: math.abs( value ) + + This function returns the absolute value of `value`. For example, `math.abs(-5)` returns 5. + + :param value: int or float, the number to get the absolute value of + :return: The absolute value of value + :syntax: + + .. code-block:: lua + + math.abs( value ) + +.. lua:function:: math.acos( value ) + + This function returns the arc cosine of `value` in radians + + :param value: int or float, compute the arc cosine of this number + :return: The arc cosine of value in radians + :syntax: + + .. code-block:: lua + + math.acos( value ) + +.. lua:function:: math.asin( value ) + + This function returns the arc sine of `value` in radians + + :param value: int or float, compute the arc sine of this number + :return: The arc sine of value in radians + :syntax: + + .. code-block:: lua + + math.asin( value ) + +.. lua:function:: math.atan( y, x ) + + Returns the arc tangent of y/x (in radians), using the signs of both arguments to find the quadrant of the result. It also handles correctly the case of x being zero + + The default value for x is 1, so that the call math.atan(y) returns the arc tangent of y + + :param y: numerator for arc tangent + :type y: number + :param x: denominator for arc tangent (defaults to 1) + :type x: number + :return: The arc tangent of y/x in radians + :syntax: + + .. code-block:: lua + + math.atan( y ) + math.atan( y, x ) + +.. lua:function:: math.ceil( value ) + + This function returns the smallest integer larger than or equal to `value`. This rounds a number up to the nearest integer. For example, `math.ceil(5.2)` returns 6. + + :param value: int or float, compute the smallest integer larger than or equal to this number + :return: The smallest integer larger than or equal to value + :rtype: integer + :syntax: + + .. code-block:: lua + + math.ceil( value ) + +.. lua:function:: math.cos( value ) + + This function returns the cosine of `value` (assumed to be in radians). + + :param value: int or float, compute the cosine of this number + :return: Cosine of value + :syntax: + + .. code-block:: lua + + math.cos( value ) + +.. lua:function:: math.cosh( value ) + + This function returns hyperbolic cosine of `value`. + + :param value: int or float, compute the hyperbolic cosine of this number + :return: Hyperbolic cosine of value + :syntax: + + .. code-block:: lua + + math.cosh( value ) + +.. lua:function:: math.deg( value ) + + This function returns the angle specified by `value` (given in radians) in degrees. + + :param value: int or float, angle in radians to convert to degrees + :return: Angle specified by value (in radians) in degrees + :syntax: + + .. code-block:: lua + + math.deg( value ) + +.. lua:function:: math.rad( value ) + + This function returns the angle specified by `value` (given in degrees) in radians. + + :param value: int or float, angle in degrees to convert to radians + :return: Angle specified by value (in degrees) in radians + :syntax: + + .. code-block:: lua + + math.rad( value ) + +.. lua:function:: math.exp( value ) + + This function returns **e** raised to `value` + + :param value: int or float, exponent of e + :return: e raised to the power of value + :syntax: + + .. code-block:: lua + + math.exp( value ) + +.. lua:function:: math.floor( value ) + + This function returns the largest integer smaller than or equal to `value`. This rounds a number down to the nearest integer. For example, `math.floorTh(5.7)` returns 5. + + :param value: int or float, compute the largest integer smaller than or equal to this number + :return: The largest integer smaller than or equal to value + :rtype: integer + :syntax: + + .. code-block:: lua + + math.floor( value ) + +.. lua:function:: math.fmod( x, y ) + + This function returns the remainder of the division of `x` by `y` that rounds the quotient towards zero. + + :param x: int or float + :param y: int or float + :return: The remainder of the division of x by y + :syntax: + + .. code-block:: lua + + math.fmod( x, y ) + +.. lua:function:: math.frexp( value ) + + This function returns m and e such that `value` = m2^e, e is an integer and the absolute value of m is in the range [0.5, 1) (or zero when x is zero). + + :param value: int or float + :return: m and e such that value = m2^e, e is an integer and the absolute value of m is in the range [0.5, 1) (or zero when x is zero). + :rtype: integer + :syntax: + + .. code-block:: lua + + math.frexp( value ) + +.. lua:function:: math.ldexp( m, e ) + + This function returns m2^e (e should be an integer) + + :param m: int or float + :param e: int + :return: m2^e + :syntax: + + .. code-block:: lua + + math.ldexp( m, e ) + +.. lua:function:: math.log( value ) + + This function returns the natural logarithm of `value` + + :param value: int or float, compute the natural logarithm of this value + :return: The natural logarithm of value + :syntax: + + .. code-block:: lua + + math.log( value ) + +.. lua:function:: math.log10( value ) + + This function returns the base-10 logarithm of `value` + + :param value: int or float, compute the base-10 logarithm of this value + :return: The base-10 logarithm of value + :syntax: + + .. code-block:: lua + + math.log10( value ) + +.. lua:function:: math.max( value, ... ) + + This function returns maximum value among its arguments. + + :param value: any comparable value + :return: The maximum value among its arguments + :syntax: + + .. code-block:: lua + + math.max( value, ... ) + +.. lua:function:: math.min( value, ... ) + + This function returns minimum value among its arguments. + + :param value: any comparable value + :return: The minimum value among its arguments + :syntax: + + .. code-block:: lua + + math.min( value, ... ) + +.. lua:function:: math.modf( value ) + + This function returns two numbers, the integral part of `value` and the fractional part of `value`. + + :param value: int or float + :return: Two numbers, the integral part of value and the fractional part of value + :syntax: + + .. code-block:: lua + + math.modf( value ) + +.. lua:function:: math.random() + + When called without arguments, `math.random()` returns a uniform pseudo-random real number in the range [0, 1). When called with an integer number `maximum`, `math.random()` returns a uniform pseudo-random integer in the range [1, maximum]. When called with two integer numbers, `minimum` and `maximum`, `math.random()` returns a uniform pseudo-random integer in the range [minimum, maximum]. + + :param minimum: int, minimum value of returned pseudo-random number + :param maximum: int, maximum value of returned pseudo-random number + :return: A uniform pseudo-random real number or integer (depending on parameters) + :rtype: integer + :syntax: + + .. code-block:: lua + + math.random() + math.random( maximum ) + math.random( minimum, maximum ) + +.. lua:function:: math.randomseed( value ) + + Sets value as the "seed" for the pseudo-random number generator. Equal seeds produce equal sequences of numbers. + + :param value: int, seed of the pseudo-random number generator + :return: A uniform pseudo-random real number or integer (depending on parameters) + :rtype: integer + :syntax: + + .. code-block:: lua + + math.randomseed( value ) + +.. lua:function:: math.sin( value ) + + This function returns the sine of `value` (assumed to be in radians). + + :param value: int or float, compute the sine of this number + :return: Sine of value + :syntax: + + .. code-block:: lua + + math.sin( value ) + +.. lua:function:: math.sinh( value ) + + This function returns hyperbolic sine of `value`. + + :param value: int or float, compute the hyperbolic sine of this number + :return: Hyperbolic sine of value + :syntax: + + .. code-block:: lua + + math.sinh( value ) + +.. lua:function:: math.tan( value ) + + This function returns the tangent of `value` (assumed to be in radians). + + :param value: int or float, compute the tangent of this number + :return: Tangent of value + :syntax: + + .. code-block:: lua + + math.tan( value ) + +.. lua:function:: math.tanh( value ) + + This function returns hyperbolic tangent of `value`. + + :param value: int or float, compute the hyperbolic tangent of this number + :return: Hyperbolic tangent of value + :syntax: + + .. code-block:: lua + + math.tanh( value ) + +.. lua:function:: math.sqrt( value ) + + This function computes the square root of `value`. You can also use the expression `value^0.5` to compute this value. + + :param value: int or float, compute the square root of this number + :return: Square root of value + :syntax: + + .. code-block:: lua + + math.sqrt( value ) + +.. lua:function:: math.tointeger( value ) + + If `value` is convertible to an integer, returns that integer. Otherwise returns **nil**. + + :param value: float, value to convert to integer + :return: `value` converted to an integer, or nil + :rtype: integer + :syntax: + + .. code-block:: lua + + math.tointeger( value ) + +.. lua:function:: math.type( value ) + + This function returns "integer" if `value` is an integer, "float" if it is a float, or **nil** if `value` is not a number. + + :param value: int or float, get the type of this value + :return: "integer" or "float" + :rtype: integer + :syntax: + + .. code-block:: lua + + math.type( value ) + +.. lua:function:: math.ult( m, n ) + + This function returns a boolean, true if integer `m` is below integer `n` when they are compared as unsigned integers. + + :param m: value for m + :type m: integer + :param n: value for n + :type n: integer + :return: true if `m` is below integer `n` when compared as unsigned + :rtype: boolean + :syntax: + + .. code-block:: lua + + math.ult( m, n ) + +.. lua:attribute:: math.huge: const + + .. symbol:: lua-api const + + A value larger than or equal to any other numerical value. + + :return: A value larger than or equal to any other numerical value + :syntax: + + .. code-block:: lua + + math.huge + +.. lua:attribute:: math.pi: const + + .. symbol:: lua-api const + + The value of **pi**. + + :return: Value of pi + :syntax: + + .. code-block:: lua + + math.pi + +.. lua:attribute:: math.maxinteger: const + + .. symbol:: lua-api const + + Specifies an integer with the maximum value for an integer + + :return: Maximum value for an integer + :rtype: integer + :syntax: + + .. code-block:: lua + + math.maxinteger + +.. lua:attribute:: math.mininteger: const + + .. symbol:: lua-api const + + Specifies an integer with the minimum value for an integer + + :return: Minimum value for an integer + :rtype: integer + :syntax: + + .. code-block:: lua + + math.mininteger + +Date and Time +------------- + +.. lua:function:: os.clock() + + Returns an approximation of the amount in seconds of CPU time used by the program. + + :return: Approximation of the amount in seconds of CPU time used by the program. + :rtype: number + :syntax: + + .. code-block:: lua + + os.clock() + +.. lua:function:: os.difftime( t2, t1 ) + + Returns the number of seconds from time `t1` to time `t2`. In POSIX, Windows, and some other systems, this value is exactly `t2-t1`. + + :param t2: Ending time + :param t1: Starting time + :return: Number of seconds from time `t1` to time `t2`. + :rtype: number + :syntax: + + .. code-block:: lua + + os.difftime( t2, t1 ) + +.. lua:function:: os.date( format ) + + Returns a string or a table containing the date and time, formatted according to the given string `format`. + + If the `time` argument is present, this is the time to be formatted (see the `os.time` function for a description of this value). Otherwise, `date` formats the current time. + + If format starts with '!', then the date is formatted in Coordinated Universal Time. After this optional character, if format is the string ``*t``, then date returns a table with the following fields: year (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), and isdst (daylight saving flag, a boolean). + + If `format` is not ``*t``, then `date` returns the date as a string, formatted according to the same rules as the C function `strftime`. + + When called without arguments, date returns a reasonable date and time representation that depends on the host system and on the current locale (that is, `os.date()` is equivalent to `os.date('%c')`). + + :param format: String used to format the returned date + :param time: If the time argument is present, this is the time to be formatted (see the os.time function for a description of this value). + :return: A string or a table containing the date and time. + :rtype: string + :syntax: + + .. code-block:: lua + + os.date() + os.date( format ) + os.date( format, time ) + +.. lua:function:: os.setlocale( locale ) + + Sets the current locale of the program. `locale` is a string specifying a locale; `category` is an optional string describing which category to change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category is "all". The function returns the name of the new locale, or nil if the request cannot be honored. + + If `locale` is the empty string, the current locale is set to an implementation-defined native locale. If `locale` is the string "C", the current locale is set to the standard C locale. + + When called with `nil` as the first argument, this function only returns the name of the current locale for the given category. + + :param locale: String specifying a locale, can be nil or the empty string. + :param category: String specifying a category to set, can be "all", "collate", "ctype", "monetary", "numeric", or "time" + :return: When called with `nil` for the first argument, returns the name of the current locale for the given category. + :syntax: + + .. code-block:: lua + + os.setlocale( locale ) + os.setlocale( locale, category ) + +.. lua:function:: os.time() + + Returns the current time when called without arguments, or a time representing the date and time specified by the given table. This table must have fields `year`, `month`, and `day`, and may have fields `hour`, `min`, `sec`, and `isdst` (for a description of these fields, see the os.date function). + + The returned value is a number, whose meaning depends on your system. In POSIX, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch"). In other systems, the meaning is not specified, and the number returned by `time` can be used only as an argument to `date` and `difftime`. + + :param table: This table must have fields `year`, `month`, and `day`, and may have fields `hour`, `min`, `sec`, and `isdst` + :return: A number, whose meaning depends on your system. In POSIX, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch"). In other systems, the meaning is not specified, and the number returned by `time` can be used only as an argument to `date` and `difftime`. + :rtype: number + :syntax: + + .. code-block:: lua + + os.time() + os.time( table ) + +Objective-C +----------- + +.. lua:attribute:: objc: table + + Exposes native Objective-C classes. + + + **Native Classes** + + + To access a native Objective-C class, append its name to `objc`. + + .. code-block:: lua + + -- access the UIScreen class + UIScreen = objc.UIScreen + + + + **Constructor** + + + If the native class has a `new` constructor, you can invoke it directly instead of calling the `new` method. + + .. code-block:: lua + + -- create an instance of NSDateComponents + dateComponents = objc.NSDateComponents() + + + + **Properties** + + + Properties can be read and modified directly. + + .. code-block:: lua + + -- read the screen brightness + brightness = objc.UIScreen.mainScreen.brightness + + -- change the screen brightness + objc.UIScreen.mainScreen.brightness = 0.5 + + + + **Methods** + + + Methods are invoked using the ':' operator and their full selector name, including named arguments separated by underscores '_' instead of colons. Note that the Objective-C method names must always end with an underscore in Codea. The underscores are required for Codea to find the corresponding signatures in Objective-C. + + .. code-block:: lua + + -- Calling a method with multiple arguments + controller:presentViewController_animated_completion_(newController, true, nil) + + -- Calling a method with no argument + webView:goBack_() + + + + **Callbacks** + + + Callbacks such as completion handlers with up to 7 arguments are supported by passing a function where each parameter is prefixed to indicate the corresponding native type. + + The following prefixes are supported for callback arguments: + + .. code-block:: lua + + c: char + i: int, NSInteger + uc: unsigned char + ui: unsigned int, NSUInteger + f: float + d: double, CGFloat + b: bool, BOOL + s: char* + o: NSObject, NSString, etc. + + + Note that only the first one or two characters are important in your argument names. `bGranted` and `boolGranted` will both work for a boolean argument, but for our examples, we decided to go with the second option. + + Struct members are passed separately. For example, for an NSRange argument, you could use intLocation and intLength arguments. + + Pointers for value types are also supported by prefixing them with `p`, (e.g. `pboolStop`). The actual value can be accessed and modified using `.value`. + + Note that when using objc from within a callback, changes are not guaranteed to occur on the main thread. Consider using `objc.async` inside your callback if you need changes to happen on the main thread, such as modifying UIControls. + + **Automatic Conversions** + + Some Codea types will be converted to corresponding Objective types automatically when passed to native methods or properties. + + **color**: UIColor + **vec2**: CGPoint + + .. code-block:: lua + + --- uiView: objc.UIView + uiView.backgroundColor = Color(255, 0, 0) + + --- uiTextView: objc.UITextView + uiTextView:setContentOffset_(vec2(0, 100)) + + :syntax: + + .. code-block:: lua + + objc.ClassName + +.. lua:function:: objc.delegate + + Returns a type which can be instantiated and used as an Objective-C delegate for the specified type. + + :return: A type to be used as an Objective-C delegate. + :syntax: + + .. code-block:: lua + + objc.delegate("DelegateName") + +.. lua:function:: objc.class + + Returns a type which can be instantiated and used as an Objective-C class, for example combined with a selector when registering for notifications through the NSNotificationCenter. + + :return: A type to be used as an Objective-C class. + :syntax: + + .. code-block:: lua + + objc.class("ClassName") + +.. lua:function:: objc.selector + + Returns an Objective-C selector with the specified name which can be used in combination with an objc.class, for example to register for notifications through the NSNotificationCenter. + + :return: An Objective-C selector (or SEL). + :syntax: + + .. code-block:: lua + + objc.selector("SelectorName") + +.. lua:function:: objc.set + + Returns an Objective-C NSSet initialized from a Lua table. + + By default, NSSet returned from calls to Objective-C (or reading properties) are automatically converted to Lua tables. If you need to use the NSSet, you can convert the table to NSSet using `objc.set`. + + :return: An Objective-C NSSet. + :syntax: + + .. code-block:: lua + + objc.set({ 1, 2, 3}) + +.. lua:function:: objc.string + + Returns an Objective-C NSString initialized from a Lua string. + + By default, strings returned from calls to Objective-C (or reading properties) are automatically converted to Lua strings and vice versa. If you need to access NSString methods, you can convert the strings to NSString using `objc.string`. + + :return: An Objective-C NSString. :rtype: string + :syntax: + + .. code-block:: lua + + objc.string("Text") + +.. lua:attribute:: objc.enum: table + + Exposes native Objective-C enumerations. + + When value names are prefixed with their enumeration's name, the prefix is removed to simplify their usage. + + For example, `objc.enum.NLTokenUnit.paragraph` is the integer value for `NLTokenUnitParagraph` (`2`). + + Unnamed enum values can be found directly under objc.enum, e.g. objc.enum.NSUTF8StringEncoding + + :syntax: + + .. code-block:: lua + + objc.enum.EnumName.ValueName + +.. lua:attribute:: objc.app: table + + The UIApplication's `sharedApplication`. + + :syntax: + + .. code-block:: lua + + objc.app + +.. lua:attribute:: objc.viewer: table + + The runtime `UIViewController`. + + :syntax: + + .. code-block:: lua + + objc.viewer + +.. lua:attribute:: objc.info: table + + Exposes the info dictionary keys and values. + + For better readability, all keys have their Apple prefix removed. + + For example, to get the value of NSBundleIdentifier, use `objc.info.bundleIdentifier`. + + :syntax: + + .. code-block:: lua + + objc.info.Key + +.. lua:function:: objc.insets( top, left, bottom, right ) + + Create a UIEdgeInsets. + + :param top: top value of the UIEdgeInsets + :param left: left value of the UIEdgeInsets + :param bottom: bottom value of the UIEdgeInsets + :param right: right value of the UIEdgeInsets + :syntax: + + .. code-block:: lua + + objc.insets( top, left, bottom, right ) + +.. lua:function:: objc.log( message ) + + Log a message using NSLog instead of the Codea console. + + :param message: Message to display + :syntax: + + .. code-block:: lua + + objc.log( message ) + +.. lua:function:: objc.inspect( class ) + + Inspect an Objective-C class, listing its variables, properties, methods and protocols. + + Returns a table with the following information: + + **super**: the superclass which can be used as if it was accessed through `objc` + + **variables**: array of instance variables + .. code-block:: lua + **name**: name of the variable + **typeEncoding**: see [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) + **type**: user-friendly name of the variable type + + + **properties**: array of instance properties + .. code-block:: lua + + **name**: name of the property + **attributes**: see [Property Type String](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html) + **type**: user-friendly name of the property type + + + **methods**: array of instance methods .. code-block:: lua - SomeClass = class("SomeClass") - c = SomeClass(10) - print(typeof(SomeClass)) -- prints "class" - print(typeof(c)) -- prints "SomeClass" - print(typeof("Hello World!")) -- prints "string" - print(typeof(5)) -- prints "number" - print(typeof(vec3(1, 2, 3))) -- prints "vec3" - print(typeof(mat3(1, 2, 3, 4, 5, 6, 7, 8, 9))) -- prints "mat3" - print(typeof(quat(1, 2, 3, 4))) -- prints "quat" - print(typeof(color(255, 0, 0, 255))) -- prints "color" - print(typeof(image(100, 100))) -- prints "image" - print(typeof(scene.default3d())) -- prints "scene" - print(typeof(asset.builtin.Cargo_Bot.Claw_Arm)) -- prints "assetKey" + **name**: name of the methods + **returnTypeEncoding**: [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) of the method's return value + **returnType**: user-friendly name of the method's return type + **arguments**: array of method arguments + **name**: name of the arguments + **typeEncoding**: see [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) + **type**: user-friendly name of the argument's type + + + **protocols**: array of instance protocols + .. code-block:: lua + + **name**: name of the protocol + + + Class members are accessible by prefixing with `class.`, for example using `objc.inspect(myClass).class.variables` to list the class variables of myClass. + + :param class: Objective-C class to inspect. + :syntax: + + .. code-block:: lua + + objc.inspect( class ) + +.. lua:function:: objc.async( function ) + + Calls the function parameter on the main thread asynchronously. + + :param function: Parameterless function to run on the main thread. + :syntax: + + .. code-block:: lua + + objc.async( function ) + +.. lua:function:: objc.point( x, y ) + + Create a CGPoint. + + :param x: x position of the CGPoint + :param y: y position of the CGPoint + :syntax: + + .. code-block:: lua + + objc.point( x, y ) -**Table Extensions** +.. lua:function:: objc.rect( x, y, width, height ) -.. lua:class:: table + Create a CGRect. - The following are extensions to the Lua table class. + :param x: x position of the CGRect + :param y: y position of the CGRect + :param width: width of the CGRect + :param height: height of the CGRect + :syntax: - .. lua:function:: flatten(t) + .. code-block:: lua + + objc.rect( x, y, width, height ) + +.. lua:function:: objc.size( width, height ) + + Create a CGSize. + + :param width: width of the CGSize + :param height: height of the CGSize + :syntax: + + .. code-block:: lua + + objc.size( width, height ) + +.. lua:function:: objc.range( loc, len ) + + Create a NSRange. + + :param loc: location of the NSRange + :param len: length of the NSRange + :syntax: + + .. code-block:: lua + + objc.range( loc, len ) + +.. lua:function:: objc.color( r, g, b, a ) + + Create a CGColor. For UIColor, use the Codea Color type instead. - Flattens a table into a single array. + :param r: red value of the CGColor + :param g: green value of the CGColor + :param b: blue value of the CGColor + :param a: alpha value of the CGColor + :syntax: - .. helptext:: flatten a nested table into one array + .. code-block:: lua + + objc.color( r, g, b, a ) + +.. lua:function:: objc.vector( dx, dy ) + + Create a CGVector. + + :param dx: x direction of the CGVector + :param dy: y direction of the CGVector + :syntax: + + .. code-block:: lua + + objc.vector( dx, dy ) - :param t: The table to flatten - :type t: table - :return: A flattened array - :rtype: table +.. lua:function:: objc.affineTransform( a, b, c, d, tx, ty ) + + Create a [CGAffineTransform](https://developer.apple.com/documentation/corefoundation/cgaffinetransform?language=objc). + + :param a: a value of the CGAffineTransform + :param b: b value of the CGAffineTransform + :param c: c value of the CGAffineTransform + :param d: d value of the CGAffineTransform + :param tx: tx value of the CGAffineTransform + :param ty: ty value of the CGAffineTransform + :syntax: .. code-block:: lua - t = {1, 2, {3, 4, {5, 6}}} - print(table.flatten(t)) -- {1, 2, 3, 4, 5, 6} + objc.affineTransform( a, b, c, d, tx, ty ) + +Frameworks +~~~~~~~~~~ + +Here are some of the frameworks included with the Codea runtime. + +Refer to Apple's documentation for how to interact with them. + +.. code-block:: lua + + [ARKit](https://developer.apple.com/documentation/arkit?language=objc) + [AssetsLibrary](https://developer.apple.com/documentation/assetslibrary?language=objc) + [AudioKit](https://audiokit.io) + [AudioToolbox](https://developer.apple.com/documentation/audiotoolbox?language=objc) + [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices?language=objc) + [CFNetwork](https://developer.apple.com/documentation/cfnetwork?language=objc) + [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth?language=objc) + [CoreGraphics](https://developer.apple.com/documentation/coregraphics?language=objc) + [CoreHaptics](https://developer.apple.com/documentation/corehaptics?language=objc) + [CoreLocation](https://developer.apple.com/documentation/corelocation?language=objc) + [CoreMedia](https://developer.apple.com/documentation/coremedia?language=objc) + [CoreMIDI](https://developer.apple.com/documentation/coremidi?language=objc) + [CoreML](https://developer.apple.com/documentation/coreml?language=objc) + [CoreMotion](https://developer.apple.com/documentation/coremotion?language=objc) + [CoreText](https://developer.apple.com/documentation/coretext?language=objc) + [CoreVideo](https://developer.apple.com/documentation/corevideo?language=objc) + [FileProvider](https://developer.apple.com/documentation/fileprovider?language=objc) + [GameController](https://developer.apple.com/documentation/gamecontroller?language=objc) + [GameplayKit](https://developer.apple.com/documentation/gameplaykit?language=objc) + [GLKit](https://developer.apple.com/documentation/glkit?language=objc) + [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore?language=objc) + [MapKit](https://developer.apple.com/documentation/mapkit?language=objc) + [MediaPlayer](https://developer.apple.com/documentation/mediaplayer?language=objc) + [MessageUI](https://developer.apple.com/documentation/messageui?language=objc) + [MLCompute](https://developer.apple.com/documentation/mlcompute?language=objc) + [NaturalLanguage](https://developer.apple.com/documentation/naturallanguage?language=objc) + [OpenGLES](https://developer.apple.com/documentation/opengles?language=objc) + [PDFKit](https://developer.apple.com/documentation/pdfkit?language=objc) + [PencilKit](https://developer.apple.com/documentation/pencilkit?language=objc) + [ReplayKit](https://developer.apple.com/documentation/replaykit?language=objc) + [Social](https://developer.apple.com/documentation/social?language=objc) + [Speech](https://developer.apple.com/documentation/speech?language=objc) + [UIKit](https://developer.apple.com/documentation/uikit?language=objc) + [UserNotifications](https://developer.apple.com/documentation/usernotifications?language=objc) + [WebKit](https://developer.apple.com/documentation/webkit?language=objc) + + +For a more exhaustive list, use the example code below. diff --git a/docs/source/api/pasteboard.rst b/docs/source/api/pasteboard.rst index 4e3a4c8..d1328de 100644 --- a/docs/source/api/pasteboard.rst +++ b/docs/source/api/pasteboard.rst @@ -1,7 +1,7 @@ -pasteboard +Pasteboard ========== -Exposes pasteboard functionnalities to copy and paste strings, images, colors and URLs. +Copy and paste strings, images, colors and URLs. To minimize unnecessary notifications when accessing pasteboard data from other applications, first call ``hasStrings``, ``hasImages``, ``hasColors`` or ``hasURLs`` to check if the desired diff --git a/docs/source/api/require.rst b/docs/source/api/require.rst index f2cb6d9..0ce2c56 100644 --- a/docs/source/api/require.rst +++ b/docs/source/api/require.rst @@ -39,6 +39,7 @@ require require(asset.documents.MyOtherLibrary.MyOtherFeature) .. helptext:: import a Lua file from an asset key + .. editor:: import .. lua:class:: require diff --git a/docs/source/api/sound.rst b/docs/source/api/sound.rst index c0654ff..e61d510 100644 --- a/docs/source/api/sound.rst +++ b/docs/source/api/sound.rst @@ -5,6 +5,8 @@ sound The sound module provides a way to play and manage sound effects and background music +.. lua:currentmodule:: None + .. lua:attribute:: SOUND_COIN: const Procedural coin sound preset (SFXR) @@ -47,6 +49,8 @@ The sound module provides a way to play and manage sound effects and background .. helptext:: blip sound effect constant +.. lua:currentmodule:: sound + .. lua:staticmethod:: play(preset[, seed]) Plays a preset procedural SFXR sound effect using a given ``preset`` and optional ``seed`` diff --git a/docs/source/api/style.rst b/docs/source/api/style.rst index 4817ecd..ede0da9 100644 --- a/docs/source/api/style.rst +++ b/docs/source/api/style.rst @@ -52,6 +52,7 @@ General Sets/gets the fill color for use in vector drawing operations .. helptext:: set the fill color + .. editor:: color .. lua:function:: noFill() @@ -64,12 +65,14 @@ General Gets the current stroke color for use in vector drawing operations .. helptext:: get the stroke color + .. editor:: color .. lua:function:: stroke(color) Sets the stroke color to the specified color, or a grayscale value .. helptext:: set the stroke color + .. editor:: color :param color: The color to set the stroke to, or a grayscale value :type color: color or number @@ -82,6 +85,7 @@ General :param number alpha: The alpha value to set the stroke to .. helptext:: set the stroke color + .. editor:: color .. lua:function:: stroke(red, green, blue) @@ -92,6 +96,7 @@ General :param number blue: The blue value to set the stroke to .. helptext:: set the stroke color + .. editor:: color .. lua:function:: stroke(red, green, blue, alpha) @@ -103,6 +108,7 @@ General :param number alpha: The alpha value to set the stroke to .. helptext:: set the stroke color + .. editor:: color .. lua:function:: noStroke() @@ -115,12 +121,14 @@ General Sets the tint color for use with :lua:func:`sprite` and :lua:meth:`mesh.draw` .. helptext:: set the tint color for images drawn with sprite() + .. editor:: color .. lua:function:: tint() -> r, g, b, a Gets the current tint color .. helptext:: get the tint color + .. editor:: color .. lua:function:: pixelScaling(scale) @@ -189,6 +197,8 @@ General Constants - Shape Mode ********************** +.. lua:currentmodule:: None + .. lua:attribute:: CORNER: const .. helptext:: corner rect mode constant @@ -208,6 +218,8 @@ Constants - Shape Mode .. helptext:: radius mode constant +.. lua:currentmodule:: style + .. lua:function:: sortOrder(order) .. helptext:: set the sort order for drawing @@ -281,6 +293,8 @@ Functions Constants - Blend Modes *********************** +.. lua:currentmodule:: None + .. lua:attribute:: NORMAL: const The default blend mode (alpha blended transparency) @@ -449,6 +463,8 @@ Constants - Blend Factors .. helptext:: source alpha saturate blend factor constant +.. lua:currentmodule:: style + Viewport ######## @@ -542,6 +558,8 @@ A stencil state is configured using a table with the following properties: Stencil Test ************ +.. lua:currentmodule:: None + .. lua:attribute:: STENCIL_TEST_LESS: const .. helptext:: less-than stencil test constant @@ -595,6 +613,8 @@ Stencil Operations .. helptext:: invert stencil operation constant +.. lua:currentmodule:: style + Text Style ########## @@ -613,38 +633,61 @@ Text Style Constants - Text **************** +.. lua:currentmodule:: None + .. lua:attribute:: LEFT: const + .. symbol:: const + :group: text-alignment + .. helptext:: left alignment constant .. lua:attribute:: CENTER: const + .. symbol:: const + :group: text-alignment + .. helptext:: center mode constant .. lua:attribute:: RIGHT: const + .. symbol:: const + :group: text-alignment + .. helptext:: right alignment constant .. lua:attribute:: TOP: const + .. symbol:: const + :group: text-alignment + .. helptext:: top alignment constant .. lua:attribute:: MIDDLE: const + .. symbol:: const + :group: text-alignment + .. helptext:: middle alignment constant .. lua:attribute:: BOTTOM: const + .. symbol:: const + :group: text-alignment + .. helptext:: bottom alignment constant .. lua:attribute:: BASELINE: const + .. symbol:: const + :group: text-alignment + .. helptext:: baseline alignment constant diff --git a/docs/source/builders/luadoc.py b/docs/source/builders/luadoc.py index f025760..798b1a0 100644 --- a/docs/source/builders/luadoc.py +++ b/docs/source/builders/luadoc.py @@ -65,6 +65,9 @@ def __init__(self, builder, doc): self.current_group = None # current group for API entries self.current_name = None # subsection title — used as name for overview entries self.current_section_content = [] + self.current_symbol = None + self.symbol_stack = [] + self.desc_symbol_stack = [] def has_desc_ancestor(self, node): """Walk up parent chain to check for desc ancestors""" @@ -75,6 +78,9 @@ def has_desc_ancestor(self, node): current = current.parent return False + def visit_section(self, node): + self.symbol_stack.append(self.current_symbol) + def visit_paragraph(self, node): if isinstance(node.parent, section) and not self.has_desc_ancestor(node): self.current_section_content.append(OverviewContent(DocutilsUtils.markdown_children(node), OverviewContentKind.TEXT)) @@ -113,6 +119,10 @@ def visit_title(self, node): def depart_section(self, node): self.flush_content() + self.current_symbol = self.symbol_stack.pop() if self.symbol_stack else None + + def extract_symbol(self, node): + return DocutilsUtils.extract_symbol(node) def flush_content(self): if self.current_section_content: @@ -138,24 +148,38 @@ def unknown_visit(self, node): if hasattr(node, 'attributes'): objtype = node.attributes.get('objtype') + if getattr(node, 'tagname', None) == 'symbol' and not self.has_desc_ancestor(node): + self.current_symbol = { + 'types': node.attributes['types'] + } + group = node.attributes.get('group') + if group: + self.current_symbol['group'] = group + return + # Flush content before any Lua object if objtype: self.flush_content() + symbol = self.extract_symbol(node) or self.current_symbol + self.desc_symbol_stack.append(self.current_symbol) + self.current_symbol = symbol + else: + symbol = self.current_symbol if objtype == 'method': - method = LuaFunction(node, 'method', self.current_group) + method = LuaFunction(node, 'method', self.current_group, symbol) self.add_to_current_scope(method) elif objtype == 'class': - cls = LuaClass(node, self.current_group) + cls = LuaClass(node, self.current_group, symbol=symbol) self.add_to_current_scope(cls) self.class_stack.append(cls) elif objtype == 'function': - self.entries.append(LuaFunction(node, 'function', self.current_group)) + self.entries.append(LuaFunction(node, 'function', self.current_group, symbol)) elif objtype == 'attribute' or objtype == 'classattribute': - attribute = LuaAttribute(node, objtype, self.current_group) + attribute = LuaAttribute(node, objtype, self.current_group, symbol=symbol) self.add_to_current_scope(attribute) # Check if this attribute should create an anonymous LuaClass @@ -164,7 +188,7 @@ def unknown_visit(self, node): lua_class_name = f"table#{attribute.module}#{attribute.name}" else: lua_class_name = f"table#{attribute.name}" - lua_class = LuaClass(name=lua_class_name, description=attribute.description, module=attribute.module, group=self.current_group) + lua_class = LuaClass(name=lua_class_name, description=attribute.description, module=attribute.module, group=self.current_group, symbol=symbol) fields = attribute.extract_fields(node) if fields: @@ -175,16 +199,18 @@ def unknown_visit(self, node): attribute.type = lua_class.name elif objtype == 'classattribute': - self.add_to_current_scope(LuaAttribute(node, objtype, self.current_group)) + self.add_to_current_scope(LuaAttribute(node, objtype, self.current_group, symbol=symbol)) elif objtype == 'staticmethod': - method = LuaFunction(node, 'staticmethod', self.current_group) + method = LuaFunction(node, 'staticmethod', self.current_group, symbol) self.add_to_current_scope(method) def unknown_departure(self, node): if hasattr(node, 'attributes'): if node.attributes.get('objtype') == 'class': self.class_stack.pop() + if node.attributes.get('objtype') and self.desc_symbol_stack: + self.current_symbol = self.desc_symbol_stack.pop() def generic_visit(self, node): pass @@ -210,4 +236,3 @@ def get_attributes(obj): # and also not any of the built-in Python attributes (typically named with double underscores). attributes = inspect.getmembers(obj, lambda a: not(inspect.isroutine(a))) return [a for a in attributes if not (a[0].startswith('__') and a[0].endswith('__'))] - diff --git a/docs/source/builders/luastruct.py b/docs/source/builders/luastruct.py index 2ae3e2a..da5d4f7 100644 --- a/docs/source/builders/luastruct.py +++ b/docs/source/builders/luastruct.py @@ -62,6 +62,28 @@ def extract_visibility(node): if visibility_node: return visibility_node.attributes['value'] return None + + @staticmethod + def extract_editor(node): + editor_node = next((child for child in node.traverse() if child.tagname == 'editor'), None) + if editor_node: + return { + 'roles': editor_node.attributes['roles'] + } + return None + + @staticmethod + def extract_symbol(node): + symbol_node = next((child for child in node.traverse() if child.tagname == 'symbol'), None) + if symbol_node: + symbol = { + 'types': symbol_node.attributes['types'] + } + group = symbol_node.attributes.get('group') + if group: + symbol['group'] = group + return symbol + return None @staticmethod def extract_module(node): @@ -269,7 +291,7 @@ def to_dict(self): } class LuaClass: - def __init__(self, node=None, group=None, name=None, description=None, module=None, helptext=None, syntax=None, parameters=None, examples=None, visibility=None): + def __init__(self, node=None, group=None, name=None, description=None, module=None, helptext=None, syntax=None, parameters=None, examples=None, visibility=None, editor=None, symbol=None): if node: # Initialize from a node self.name = DocutilsUtils.extract_name(node) @@ -280,6 +302,8 @@ def __init__(self, node=None, group=None, name=None, description=None, module=No self.parameters = DocutilsUtils.extract_parameters(node, True) self.examples = DocutilsUtils.extract_code_samples(node) self.visibility = DocutilsUtils.extract_visibility(node) + self.editor = DocutilsUtils.extract_editor(node) + self.symbol = DocutilsUtils.extract_symbol(node) or symbol self.group = group else: # Initialize from provided parameters @@ -291,6 +315,8 @@ def __init__(self, node=None, group=None, name=None, description=None, module=No self.parameters = parameters if parameters else [] self.examples = examples if examples else [] self.visibility = visibility + self.editor = editor + self.symbol = symbol self.group = group self.members = [] @@ -313,6 +339,10 @@ def to_dict(self): } if self.visibility is not None: d['visibility'] = self.visibility + if self.editor is not None: + d['editor'] = self.editor + if self.symbol is not None: + d['symbol'] = self.symbol return d class LuaParameter: @@ -352,7 +382,7 @@ def to_dict(self): } class LuaFunction: - def __init__(self, node, type, group=None): + def __init__(self, node, type, group=None, symbol=None): self.name = DocutilsUtils.extract_name(node) self.module = DocutilsUtils.extract_module(node) self.description = DocutilsUtils.extract_description(node) @@ -362,6 +392,8 @@ def __init__(self, node, type, group=None): self.syntax = DocutilsUtils.extract_syntax(node) self.examples = DocutilsUtils.extract_code_samples(node) self.visibility = DocutilsUtils.extract_visibility(node) + self.editor = DocutilsUtils.extract_editor(node) + self.symbol = DocutilsUtils.extract_symbol(node) or symbol self.returns = self.extract_returns(node) self.type = type @@ -404,11 +436,15 @@ def to_dict(self): } if self.visibility is not None: d['visibility'] = self.visibility + if self.editor is not None: + d['editor'] = self.editor + if self.symbol is not None: + d['symbol'] = self.symbol return d class LuaAttribute: - def __init__(self, node=None, kind=None, group=None, name=None, type=None, module=None, description=None, helptext=None, syntax=None, examples=None, visibility=None): + def __init__(self, node=None, kind=None, group=None, name=None, type=None, module=None, description=None, helptext=None, syntax=None, examples=None, visibility=None, editor=None, symbol=None): if node: # Initialize from a node self.name = DocutilsUtils.extract_name(node) @@ -421,6 +457,8 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.description = DocutilsUtils.extract_description(node) self.helptext = DocutilsUtils.extract_helptext(node) self.visibility = DocutilsUtils.extract_visibility(node) + self.editor = DocutilsUtils.extract_editor(node) + self.symbol = DocutilsUtils.extract_symbol(node) or symbol self.kind = kind self.group = group else: @@ -435,6 +473,8 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.default_value = None self.readonly = False self.visibility = visibility + self.editor = editor + self.symbol = symbol self.group = group self.kind = kind if kind else 'attribute' @@ -520,6 +560,10 @@ def to_dict(self): } if self.visibility is not None: d['visibility'] = self.visibility + if self.editor is not None: + d['editor'] = self.editor + if self.symbol is not None: + d['symbol'] = self.symbol return d @@ -558,4 +602,4 @@ def to_dict(self): return { 'type': self.type, 'content': self.content - } \ No newline at end of file + } diff --git a/docs/source/conf.py b/docs/source/conf.py index a948943..df114b7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -36,7 +36,9 @@ 'sphinx_copybutton', 'sphinx_toolbox.collapse', 'helptext', - 'visibility' + 'visibility', + 'editor', + 'codeasymbol' ] # Add any paths that contain templates here, relative to this directory. @@ -68,4 +70,3 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] - diff --git a/docs/source/extensions/codeasymbol.py b/docs/source/extensions/codeasymbol.py new file mode 100644 index 0000000..84eb924 --- /dev/null +++ b/docs/source/extensions/codeasymbol.py @@ -0,0 +1,36 @@ +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx.util.docutils import SphinxDirective + +class SymbolNode(nodes.General, nodes.Element): + tagname = 'symbol' + pass + +class SymbolDirective(SphinxDirective): + """A directive to attach Codea symbol classification metadata to an API entry""" + + optional_arguments = 16 + final_argument_whitespace = True + has_content = True + option_spec = { + 'group': directives.unchanged + } + + def run(self): + node = SymbolNode() + source = ' '.join([*self.arguments, *self.content]) + node['types'] = [symbol_type.strip() for symbol_type in source.replace(',', ' ').split() if symbol_type.strip()] + group = self.options.get('group') + if group: + node['group'] = group + return [node] + +def html_visit_symbol_node(self, node): + pass + +def html_depart_symbol_node(self, node): + pass + +def setup(app): + app.add_node(SymbolNode, html=(html_visit_symbol_node, html_depart_symbol_node)) + app.add_directive('symbol', SymbolDirective) diff --git a/docs/source/extensions/editor.py b/docs/source/extensions/editor.py new file mode 100644 index 0000000..41aee06 --- /dev/null +++ b/docs/source/extensions/editor.py @@ -0,0 +1,29 @@ +from docutils import nodes +from sphinx.util.docutils import SphinxDirective + +class EditorNode(nodes.General, nodes.Element): + tagname = 'editor' + pass + +class EditorDirective(SphinxDirective): + """A directive to attach Codea editor affordance roles to an API entry""" + + optional_arguments = 16 + final_argument_whitespace = True + has_content = True + + def run(self): + node = EditorNode() + source = ' '.join([*self.arguments, *self.content]) + node['roles'] = [role.strip() for role in source.replace(',', ' ').split() if role.strip()] + return [node] + +def html_visit_editor_node(self, node): + pass + +def html_depart_editor_node(self, node): + pass + +def setup(app): + app.add_node(EditorNode, html=(html_visit_editor_node, html_depart_editor_node)) + app.add_directive('editor', EditorDirective) From 1d48e365c1ea4329a51443af735ef6411cb3cceb Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Tue, 28 Apr 2026 12:41:16 +0930 Subject: [PATCH 05/11] Updates to structure and private class impl --- README.md | 42 +++ docs/source/api/lua.rst | 448 ------------------------------ docs/source/api/objc.rst | 25 +- docs/source/builders/luadoc.py | 21 +- docs/source/builders/luastruct.py | 33 ++- docs/source/chapters_config.json | 57 ++-- 6 files changed, 145 insertions(+), 481 deletions(-) diff --git a/README.md b/README.md index e9551eb..53a1ffa 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,48 @@ Constants Here `style.fill` is namespaced, `LEFT` is global, and later functions return to the `style` namespace. +### Structural Table Return Types + +Use `:rtype: table$private.` when a function returns a generic Lua table +but Codea should understand the specific fields of that returned table for +tooling. Text before `$` is the documentation-facing type. Text after `$` is the +lookup type. `private.` is module-relative, so inside an `objc` module it +becomes `objc.private.` in Codea's JSON. + +```rst +.. lua:module:: objc + +.. lua:function:: insets(top, left, bottom, right) + + Create a UIEdgeInsets. + + :param top: top value of the UIEdgeInsets + :type top: number + :param left: left value of the UIEdgeInsets + :type left: number + :param bottom: bottom value of the UIEdgeInsets + :type bottom: number + :param right: right value of the UIEdgeInsets + :type right: number + :return: The UIEdgeInsets struct. + :rtype: table$private.insets + +.. lua:class:: private.insets + + .. visibility:: private + + .. lua:attribute:: top: number + .. lua:attribute:: left: number + .. lua:attribute:: bottom: number + .. lua:attribute:: right: number +``` + +The `luadoc` builder emits the return metadata as `type: +"objc.private.insets"` and `displayType: "table"`. Codea uses `type` for +autocomplete and type lookup, and `displayType` for Reference display. The +private class is authored explicitly so function arguments and returned table +fields can differ. + ### Symbol Annotations Use `.. symbol::` for syntax-highlighting classifications. Symbol annotations diff --git a/docs/source/api/lua.rst b/docs/source/api/lua.rst index 41d8914..c04da67 100644 --- a/docs/source/api/lua.rst +++ b/docs/source/api/lua.rst @@ -1203,451 +1203,3 @@ Date and Time os.time() os.time( table ) - -Objective-C ------------ - -.. lua:attribute:: objc: table - - Exposes native Objective-C classes. - - - **Native Classes** - - - To access a native Objective-C class, append its name to `objc`. - - .. code-block:: lua - - -- access the UIScreen class - UIScreen = objc.UIScreen - - - - **Constructor** - - - If the native class has a `new` constructor, you can invoke it directly instead of calling the `new` method. - - .. code-block:: lua - - -- create an instance of NSDateComponents - dateComponents = objc.NSDateComponents() - - - - **Properties** - - - Properties can be read and modified directly. - - .. code-block:: lua - - -- read the screen brightness - brightness = objc.UIScreen.mainScreen.brightness - - -- change the screen brightness - objc.UIScreen.mainScreen.brightness = 0.5 - - - - **Methods** - - - Methods are invoked using the ':' operator and their full selector name, including named arguments separated by underscores '_' instead of colons. Note that the Objective-C method names must always end with an underscore in Codea. The underscores are required for Codea to find the corresponding signatures in Objective-C. - - .. code-block:: lua - - -- Calling a method with multiple arguments - controller:presentViewController_animated_completion_(newController, true, nil) - - -- Calling a method with no argument - webView:goBack_() - - - - **Callbacks** - - - Callbacks such as completion handlers with up to 7 arguments are supported by passing a function where each parameter is prefixed to indicate the corresponding native type. - - The following prefixes are supported for callback arguments: - - .. code-block:: lua - - c: char - i: int, NSInteger - uc: unsigned char - ui: unsigned int, NSUInteger - f: float - d: double, CGFloat - b: bool, BOOL - s: char* - o: NSObject, NSString, etc. - - - Note that only the first one or two characters are important in your argument names. `bGranted` and `boolGranted` will both work for a boolean argument, but for our examples, we decided to go with the second option. - - Struct members are passed separately. For example, for an NSRange argument, you could use intLocation and intLength arguments. - - Pointers for value types are also supported by prefixing them with `p`, (e.g. `pboolStop`). The actual value can be accessed and modified using `.value`. - - Note that when using objc from within a callback, changes are not guaranteed to occur on the main thread. Consider using `objc.async` inside your callback if you need changes to happen on the main thread, such as modifying UIControls. - - **Automatic Conversions** - - Some Codea types will be converted to corresponding Objective types automatically when passed to native methods or properties. - - **color**: UIColor - **vec2**: CGPoint - - .. code-block:: lua - - --- uiView: objc.UIView - uiView.backgroundColor = Color(255, 0, 0) - - --- uiTextView: objc.UITextView - uiTextView:setContentOffset_(vec2(0, 100)) - - :syntax: - - .. code-block:: lua - - objc.ClassName - -.. lua:function:: objc.delegate - - Returns a type which can be instantiated and used as an Objective-C delegate for the specified type. - - :return: A type to be used as an Objective-C delegate. - :syntax: - - .. code-block:: lua - - objc.delegate("DelegateName") - -.. lua:function:: objc.class - - Returns a type which can be instantiated and used as an Objective-C class, for example combined with a selector when registering for notifications through the NSNotificationCenter. - - :return: A type to be used as an Objective-C class. - :syntax: - - .. code-block:: lua - - objc.class("ClassName") - -.. lua:function:: objc.selector - - Returns an Objective-C selector with the specified name which can be used in combination with an objc.class, for example to register for notifications through the NSNotificationCenter. - - :return: An Objective-C selector (or SEL). - :syntax: - - .. code-block:: lua - - objc.selector("SelectorName") - -.. lua:function:: objc.set - - Returns an Objective-C NSSet initialized from a Lua table. - - By default, NSSet returned from calls to Objective-C (or reading properties) are automatically converted to Lua tables. If you need to use the NSSet, you can convert the table to NSSet using `objc.set`. - - :return: An Objective-C NSSet. - :syntax: - - .. code-block:: lua - - objc.set({ 1, 2, 3}) - -.. lua:function:: objc.string - - Returns an Objective-C NSString initialized from a Lua string. - - By default, strings returned from calls to Objective-C (or reading properties) are automatically converted to Lua strings and vice versa. If you need to access NSString methods, you can convert the strings to NSString using `objc.string`. - - :return: An Objective-C NSString. - :rtype: string - :syntax: - - .. code-block:: lua - - objc.string("Text") - -.. lua:attribute:: objc.enum: table - - Exposes native Objective-C enumerations. - - When value names are prefixed with their enumeration's name, the prefix is removed to simplify their usage. - - For example, `objc.enum.NLTokenUnit.paragraph` is the integer value for `NLTokenUnitParagraph` (`2`). - - Unnamed enum values can be found directly under objc.enum, e.g. objc.enum.NSUTF8StringEncoding - - :syntax: - - .. code-block:: lua - - objc.enum.EnumName.ValueName - -.. lua:attribute:: objc.app: table - - The UIApplication's `sharedApplication`. - - :syntax: - - .. code-block:: lua - - objc.app - -.. lua:attribute:: objc.viewer: table - - The runtime `UIViewController`. - - :syntax: - - .. code-block:: lua - - objc.viewer - -.. lua:attribute:: objc.info: table - - Exposes the info dictionary keys and values. - - For better readability, all keys have their Apple prefix removed. - - For example, to get the value of NSBundleIdentifier, use `objc.info.bundleIdentifier`. - - :syntax: - - .. code-block:: lua - - objc.info.Key - -.. lua:function:: objc.insets( top, left, bottom, right ) - - Create a UIEdgeInsets. - - :param top: top value of the UIEdgeInsets - :param left: left value of the UIEdgeInsets - :param bottom: bottom value of the UIEdgeInsets - :param right: right value of the UIEdgeInsets - :syntax: - - .. code-block:: lua - - objc.insets( top, left, bottom, right ) - -.. lua:function:: objc.log( message ) - - Log a message using NSLog instead of the Codea console. - - :param message: Message to display - :syntax: - - .. code-block:: lua - - objc.log( message ) - -.. lua:function:: objc.inspect( class ) - - Inspect an Objective-C class, listing its variables, properties, methods and protocols. - - Returns a table with the following information: - - **super**: the superclass which can be used as if it was accessed through `objc` - - **variables**: array of instance variables - .. code-block:: lua - - **name**: name of the variable - **typeEncoding**: see [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) - **type**: user-friendly name of the variable type - - - **properties**: array of instance properties - .. code-block:: lua - - **name**: name of the property - **attributes**: see [Property Type String](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html) - **type**: user-friendly name of the property type - - - **methods**: array of instance methods - .. code-block:: lua - - **name**: name of the methods - **returnTypeEncoding**: [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) of the method's return value - **returnType**: user-friendly name of the method's return type - **arguments**: array of method arguments - **name**: name of the arguments - **typeEncoding**: see [Type Encoding](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html) - **type**: user-friendly name of the argument's type - - - **protocols**: array of instance protocols - .. code-block:: lua - - **name**: name of the protocol - - - Class members are accessible by prefixing with `class.`, for example using `objc.inspect(myClass).class.variables` to list the class variables of myClass. - - :param class: Objective-C class to inspect. - :syntax: - - .. code-block:: lua - - objc.inspect( class ) - -.. lua:function:: objc.async( function ) - - Calls the function parameter on the main thread asynchronously. - - :param function: Parameterless function to run on the main thread. - :syntax: - - .. code-block:: lua - - objc.async( function ) - -.. lua:function:: objc.point( x, y ) - - Create a CGPoint. - - :param x: x position of the CGPoint - :param y: y position of the CGPoint - :syntax: - - .. code-block:: lua - - objc.point( x, y ) - -.. lua:function:: objc.rect( x, y, width, height ) - - Create a CGRect. - - :param x: x position of the CGRect - :param y: y position of the CGRect - :param width: width of the CGRect - :param height: height of the CGRect - :syntax: - - .. code-block:: lua - - objc.rect( x, y, width, height ) - -.. lua:function:: objc.size( width, height ) - - Create a CGSize. - - :param width: width of the CGSize - :param height: height of the CGSize - :syntax: - - .. code-block:: lua - - objc.size( width, height ) - -.. lua:function:: objc.range( loc, len ) - - Create a NSRange. - - :param loc: location of the NSRange - :param len: length of the NSRange - :syntax: - - .. code-block:: lua - - objc.range( loc, len ) - -.. lua:function:: objc.color( r, g, b, a ) - - Create a CGColor. For UIColor, use the Codea Color type instead. - - :param r: red value of the CGColor - :param g: green value of the CGColor - :param b: blue value of the CGColor - :param a: alpha value of the CGColor - :syntax: - - .. code-block:: lua - - objc.color( r, g, b, a ) - -.. lua:function:: objc.vector( dx, dy ) - - Create a CGVector. - - :param dx: x direction of the CGVector - :param dy: y direction of the CGVector - :syntax: - - .. code-block:: lua - - objc.vector( dx, dy ) - -.. lua:function:: objc.affineTransform( a, b, c, d, tx, ty ) - - Create a [CGAffineTransform](https://developer.apple.com/documentation/corefoundation/cgaffinetransform?language=objc). - - :param a: a value of the CGAffineTransform - :param b: b value of the CGAffineTransform - :param c: c value of the CGAffineTransform - :param d: d value of the CGAffineTransform - :param tx: tx value of the CGAffineTransform - :param ty: ty value of the CGAffineTransform - :syntax: - - .. code-block:: lua - - objc.affineTransform( a, b, c, d, tx, ty ) - -Frameworks -~~~~~~~~~~ - -Here are some of the frameworks included with the Codea runtime. - -Refer to Apple's documentation for how to interact with them. - -.. code-block:: lua - - [ARKit](https://developer.apple.com/documentation/arkit?language=objc) - [AssetsLibrary](https://developer.apple.com/documentation/assetslibrary?language=objc) - [AudioKit](https://audiokit.io) - [AudioToolbox](https://developer.apple.com/documentation/audiotoolbox?language=objc) - [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices?language=objc) - [CFNetwork](https://developer.apple.com/documentation/cfnetwork?language=objc) - [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth?language=objc) - [CoreGraphics](https://developer.apple.com/documentation/coregraphics?language=objc) - [CoreHaptics](https://developer.apple.com/documentation/corehaptics?language=objc) - [CoreLocation](https://developer.apple.com/documentation/corelocation?language=objc) - [CoreMedia](https://developer.apple.com/documentation/coremedia?language=objc) - [CoreMIDI](https://developer.apple.com/documentation/coremidi?language=objc) - [CoreML](https://developer.apple.com/documentation/coreml?language=objc) - [CoreMotion](https://developer.apple.com/documentation/coremotion?language=objc) - [CoreText](https://developer.apple.com/documentation/coretext?language=objc) - [CoreVideo](https://developer.apple.com/documentation/corevideo?language=objc) - [FileProvider](https://developer.apple.com/documentation/fileprovider?language=objc) - [GameController](https://developer.apple.com/documentation/gamecontroller?language=objc) - [GameplayKit](https://developer.apple.com/documentation/gameplaykit?language=objc) - [GLKit](https://developer.apple.com/documentation/glkit?language=objc) - [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore?language=objc) - [MapKit](https://developer.apple.com/documentation/mapkit?language=objc) - [MediaPlayer](https://developer.apple.com/documentation/mediaplayer?language=objc) - [MessageUI](https://developer.apple.com/documentation/messageui?language=objc) - [MLCompute](https://developer.apple.com/documentation/mlcompute?language=objc) - [NaturalLanguage](https://developer.apple.com/documentation/naturallanguage?language=objc) - [OpenGLES](https://developer.apple.com/documentation/opengles?language=objc) - [PDFKit](https://developer.apple.com/documentation/pdfkit?language=objc) - [PencilKit](https://developer.apple.com/documentation/pencilkit?language=objc) - [ReplayKit](https://developer.apple.com/documentation/replaykit?language=objc) - [Social](https://developer.apple.com/documentation/social?language=objc) - [Speech](https://developer.apple.com/documentation/speech?language=objc) - [UIKit](https://developer.apple.com/documentation/uikit?language=objc) - [UserNotifications](https://developer.apple.com/documentation/usernotifications?language=objc) - [WebKit](https://developer.apple.com/documentation/webkit?language=objc) - - -For a more exhaustive list, use the example code below. diff --git a/docs/source/api/objc.rst b/docs/source/api/objc.rst index aadbef5..4853ac4 100644 --- a/docs/source/api/objc.rst +++ b/docs/source/api/objc.rst @@ -368,7 +368,7 @@ Some Codea types will be converted to corresponding Objective types automaticall isStandalone = objc.info.bundleIdentifier ~= "com.twolivesleft.Codify" -.. lua:attribute:: insets: table +.. lua:function:: insets(top, left, bottom, right) Create a UIEdgeInsets. @@ -383,13 +383,34 @@ Some Codea types will be converted to corresponding Objective types automaticall :param right: right value of the UIEdgeInsets :type right: number :return: The UIEdgeInsets struct. - :rtype: table + :rtype: table$private.insets + :syntax: .. code-block:: lua objc.insets(top, left, bottom, right) +.. lua:class:: private.insets + + .. visibility:: private + + .. lua:attribute:: top: number + + top value of the UIEdgeInsets + + .. lua:attribute:: left: number + + left value of the UIEdgeInsets + + .. lua:attribute:: bottom: number + + bottom value of the UIEdgeInsets + + .. lua:attribute:: right: number + + right value of the UIEdgeInsets + .. lua:function:: log(message) Log a message using NSLog instead of the Codea console. diff --git a/docs/source/builders/luadoc.py b/docs/source/builders/luadoc.py index 798b1a0..c9375f9 100644 --- a/docs/source/builders/luadoc.py +++ b/docs/source/builders/luadoc.py @@ -176,27 +176,33 @@ def unknown_visit(self, node): self.class_stack.append(cls) elif objtype == 'function': - self.entries.append(LuaFunction(node, 'function', self.current_group, symbol)) + function = LuaFunction(node, 'function', self.current_group, symbol) + self.entries.append(function) elif objtype == 'attribute' or objtype == 'classattribute': attribute = LuaAttribute(node, objtype, self.current_group, symbol=symbol) self.add_to_current_scope(attribute) - # Check if this attribute should create an anonymous LuaClass + # A table attribute with documented fields represents a structural + # table type for Codea's autocomplete. Keep the public attribute + # display type as `table`, but point lookup at a private module- + # scoped class that carries the field metadata. if attribute.type == 'table': if attribute.module: - lua_class_name = f"table#{attribute.module}#{attribute.name}" + lua_class_name = f"private.{attribute.name}" + lookup_type = f"{attribute.module}.{lua_class_name}" else: - lua_class_name = f"table#{attribute.name}" - lua_class = LuaClass(name=lua_class_name, description=attribute.description, module=attribute.module, group=self.current_group, symbol=symbol) + lua_class_name = f"private.{attribute.name}" + lookup_type = lua_class_name + lua_class = LuaClass(name=lua_class_name, description=attribute.description, module=attribute.module, group=self.current_group, visibility='private', symbol=symbol) fields = attribute.extract_fields(node) if fields: for field in fields: lua_class.members.append(field) self.add_to_current_scope(lua_class) - # Update the attribute's type to refer to this new anonymous class - attribute.type = lua_class.name + attribute.type = lookup_type + attribute.display_type = 'table' elif objtype == 'classattribute': self.add_to_current_scope(LuaAttribute(node, objtype, self.current_group, symbol=symbol)) @@ -224,7 +230,6 @@ def add_to_current_scope(self, element): else: self.entries.append(element) - def setup(app): app.add_builder(LuaJSONBuilder) diff --git a/docs/source/builders/luastruct.py b/docs/source/builders/luastruct.py index da5d4f7..f68f07b 100644 --- a/docs/source/builders/luastruct.py +++ b/docs/source/builders/luastruct.py @@ -368,18 +368,22 @@ def to_dict(self): } class LuaReturn: - def __init__(self, type_hint=None, description=None): + def __init__(self, type_hint=None, description=None, display_type=None): self.type_hint = type_hint self.description = description + self.display_type = display_type def __str__(self): return f"Returns `{self.type_hint}`\n\t{self.description}" def to_dict(self): - return { + d = { 'type': self.type_hint, 'description': self.description } + if self.display_type is not None: + d['displayType'] = self.display_type + return d class LuaFunction: def __init__(self, node, type, group=None, symbol=None): @@ -397,6 +401,19 @@ def __init__(self, node, type, group=None, symbol=None): self.returns = self.extract_returns(node) self.type = type + def parse_return_type(self, type_text): + if '$' not in type_text: + return type_text, None + + display_type, lookup_type = type_text.split('$', 1) + display_type = display_type.strip() + lookup_type = lookup_type.strip() + + if lookup_type.startswith('private.') and self.module: + lookup_type = f"{self.module}.{lookup_type}" + + return lookup_type, display_type + def extract_returns(self, node): returns = [] return_nodes = node.next_node(condition=lambda n: n.tagname == 'field_list') @@ -409,11 +426,13 @@ def extract_returns(self, node): # We assume there's an tag wrapping the type description for para in field.traverse(nodes.paragraph): type_text = ''.join([n.astext() for n in para.traverse() if isinstance(n, nodes.Text)]) + type_hint, display_type = self.parse_return_type(type_text) if returns: - returns[0].type_hint = type_text # Assuming only one return entry is common + returns[0].type_hint = type_hint # Assuming only one return entry is common + returns[0].display_type = display_type else: # In case the return type is specified before the description - returns.append(LuaReturn(type_hint=type_text)) + returns.append(LuaReturn(type_hint=type_hint, display_type=display_type)) return returns def __str__(self): @@ -444,7 +463,7 @@ def to_dict(self): class LuaAttribute: - def __init__(self, node=None, kind=None, group=None, name=None, type=None, module=None, description=None, helptext=None, syntax=None, examples=None, visibility=None, editor=None, symbol=None): + def __init__(self, node=None, kind=None, group=None, name=None, type=None, module=None, description=None, helptext=None, syntax=None, examples=None, visibility=None, editor=None, symbol=None, display_type=None): if node: # Initialize from a node self.name = DocutilsUtils.extract_name(node) @@ -461,6 +480,7 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.symbol = DocutilsUtils.extract_symbol(node) or symbol self.kind = kind self.group = group + self.display_type = None else: # Initialize from provided parameters self.name = name @@ -477,6 +497,7 @@ def __init__(self, node=None, kind=None, group=None, name=None, type=None, modul self.symbol = symbol self.group = group self.kind = kind if kind else 'attribute' + self.display_type = display_type def extract_type(self, node): # Finds the first 'desc_type' element and extracts its text, stripping bracket qualifiers. @@ -560,6 +581,8 @@ def to_dict(self): } if self.visibility is not None: d['visibility'] = self.visibility + if self.display_type is not None: + d['displayType'] = self.display_type if self.editor is not None: d['editor'] = self.editor if self.symbol is not None: diff --git a/docs/source/chapters_config.json b/docs/source/chapters_config.json index a7a9ce9..dfbbc70 100644 --- a/docs/source/chapters_config.json +++ b/docs/source/chapters_config.json @@ -1,4 +1,11 @@ [ + { + "id": "Codea", + "title": "Getting Started", + "subtitle": "How Codea works — setup, draw, callbacks and lifecycle", + "icon": "ChapterIconDisplay", + "entries": ["manual/codea", "manual/codea_3x"] + }, { "id": "Graphics", "title": "Graphics", @@ -21,19 +28,33 @@ "entries": ["manual/shaders", "api/mesh", "api/material", "api/shader", "api/gpu_noise_lib"] }, { - "id": "Physics", - "title": "Physics", - "subtitle": "Dynamic motion with forces, joints and collisions", + "id": "Physics2D", + "title": "Physics 2D", + "subtitle": "Dynamic motion with forces, joints and collisions in 2D", "icon": "ChapterIconPhysics", - "entries": ["manual/physics2d", "api/physics2d", "manual/physics3d", "api/physics3d"] + "entries": ["manual/physics2d", "api/physics2d"] + }, + { + "id": "Physics3D", + "title": "Physics 3D", + "subtitle": "Dynamic motion with forces, joints and collisions in 3D", + "icon": "ChapterIconPhysics", + "entries": ["manual/physics3d", "api/physics3d"] }, { "id": "Input", - "title": "Input", - "subtitle": "Responding to touches, keyboard and device motion", + "title": "Touches & Input", + "subtitle": "Responding to touches, keyboard and mouse", "icon": "ChapterIconTouch", - "entries": ["manual/input", "api/input", "api/motion"] + "entries": ["manual/input", "api/input"] }, + { + "id": "Motion", + "title": "Device Motion", + "subtitle": "Responding to device motion", + "icon": "ChapterIconAccelerometer", + "entries": ["api/motion"] + }, { "id": "Sounds", "title": "Sound", @@ -50,17 +71,17 @@ }, { "id": "Vector", - "title": "Math & Types", + "title": "Vector Math", "subtitle": "Vector, matrix and mathematical types", "icon": "ChapterIconVector", "entries": ["manual/vectors", "api/math_types", "api/matrix"] }, { "id": "Display", - "title": "UI & Viewer", + "title": "Display & Viewer", "subtitle": "User interface components and display settings", "icon": "ChapterIconParameters", - "entries": ["api/ui", "api/viewer", "api/device", "api/inspector"] + "entries": ["api/viewer", "api/device", "api/inspector", "api/ui"] }, { "id": "Animation", @@ -71,16 +92,16 @@ }, { "id": "Lua", - "title": "Lua Language", - "subtitle": "Tables, strings, math and Objective-C bridge", + "title": "Lua", + "subtitle": "Tables, strings, and math", "icon": "ChapterIconLua", - "entries": ["api/lua", "api/string", "api/require", "api/objc", "api/pasteboard"] + "entries": ["api/lua", "api/string", "api/require", "api/pasteboard"] }, { - "id": "Codea", - "title": "Codea", - "subtitle": "How Codea works — setup, draw, callbacks and lifecycle", - "icon": "ChapterIconDisplay", - "entries": ["manual/codea", "manual/codea_3x"] + "id": "ObjC", + "title": "Objective-C", + "subtitle": "Interfacing with Objective-C code", + "icon": "ChapterIconObjC", + "entries": ["api/objc"] } ] From b551361c8927ec122a0ffbee202ae4168f5763f6 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Tue, 28 Apr 2026 12:50:44 +0930 Subject: [PATCH 06/11] Improves motion docs for autocomplete --- docs/source/api/motion.rst | 99 ++++++++++++++------------------------ 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/docs/source/api/motion.rst b/docs/source/api/motion.rst index 6c9c5d9..ca29638 100644 --- a/docs/source/api/motion.rst +++ b/docs/source/api/motion.rst @@ -32,6 +32,17 @@ Motion - ``motion.referenceFrame.XMagneticNorthZVertical``: The X-axis points toward the magnetic north and the Z-axis is vertical. - ``motion.referenceFrame.XTrueNorthZVertical``: The X-axis points toward the true north and the Z-axis is vertical. +.. lua:attribute:: referenceFrame: table + + Reference frame constants for motion tracking. + + .. helptext:: motion reference frame constants + + :param const XArbitraryZVertical: The X-axis is arbitrary and the Z-axis is vertical. + :param const XArbitraryCorrectedZVertical: The X-axis is arbitrary and the Z-axis is vertical. If available, the magnetometer will be used to correct for accumulated yaw errors. + :param const XMagneticNorthZVertical: The X-axis points toward the magnetic north and the Z-axis is vertical. + :param const XTrueNorthZVertical: The X-axis points toward the true north and the Z-axis is vertical. + .. lua:function:: stop() Stop tracking motion metrics and set autoStart to false. @@ -64,17 +75,15 @@ Motion .. helptext:: current rotation rate -.. lua:attribute:: sensorLocation: integer - - The location of the device's sensors. +.. lua:attribute:: sensorLocation: table - .. helptext:: location of the motion sensors + Sensor location constants. - The value can be one of the following: + .. helptext:: motion sensor location constants - - ``motion.sensorLocation.default``: The location of the device's sensors is the default one. - - ``motion.sensorLocation.headphoneLeft``: The device's sensors are located near the left headphone. - - ``motion.sensorLocation.headphoneRight``: The device's sensors are located near the right headphone. + :param const default: The location of the device's sensors is the default one. + :param const headphoneLeft: The device's sensors are located near the left headphone. + :param const headphoneRight: The device's sensors are located near the right headphone. .. lua:attribute:: heading: number @@ -85,7 +94,7 @@ Motion Device Orientation ================== -.. lua:class:: attitude +.. lua:attribute:: attitude: table Represents a measurement of your device attitude. This orientation of a body relative to a given frame of reference. @@ -93,68 +102,32 @@ Device Orientation The value can be one of the following ``motion.referenceFrame.XArbitraryZVertical``, ``motion.referenceFrame.XArbitraryCorrectedZVertical``, ``motion.referenceFrame.XMagneticNorthZVertical``, ``motion.referenceFrame.XTrueNorthZVertical``. - :param pitch: The pitch of the device, in radians. - :type pitch: number - - :param yaw: The yaw of the device, in radians. - :type yaw: number - - :param roll: The roll of the device, in radians. - :type roll: number - - :param rotationMatrix: The rotation matrix that describes the device's orientation. - :type rotationMatrix: mat3x3 - - :param quaternion: The quaternion that describes the device's orientation. - :type quaternion: quat - - :param referenceFrame: The reference frame in which motion metrics are tracked. - :type referenceFrame: integer - - .. lua:attribute:: XArbitraryZVertical: integer - - The X-axis is arbitrary and the Z-axis is vertical. - - .. helptext:: arbitrary x axis, vertical z reference frame - - .. lua:attribute:: XArbitraryCorrectedZVertical: integer - - The X-axis is arbitrary and the Z-axis is vertical. The system will attempt to correct for the device's orientation. - - .. helptext:: corrected arbitrary x axis, vertical z reference frame - - .. lua:attribute:: XMagneticNorthZVertical: integer - - The X-axis points toward the magnetic north and the Z-axis is vertical. - - .. helptext:: magnetic north x axis, vertical z reference frame - - .. lua:attribute:: XTrueNorthZVertical: integer - - The X-axis points toward the true north and the Z-axis is vertical. - - .. helptext:: true north x axis, vertical z reference frame + :param number pitch: The pitch of the device, in radians. + :param number yaw: The yaw of the device, in radians. + :param number roll: The roll of the device, in radians. + :param mat3x3 rotationMatrix: The rotation matrix that describes the device's orientation. + :param quat quaternion: The quaternion that describes the device's orientation. + :param integer referenceFrame: The reference frame in which motion metrics are tracked. Magnetic Field Data =================== -.. lua:class:: magnetic +.. lua:attribute:: magnetic: table - .. lua:attribute:: field: vec3 + Magnetic field data. - The magnetic field vector in the device's reference frame. + .. helptext:: current magnetic field data - .. helptext:: get the magnetic field vector - - .. lua:attribute:: accuracy: integer + :param vec3 field: The magnetic field vector in the device's reference frame. + :param integer accuracy: The accuracy of the magnetic field data. - The accuracy of the magnetic field data. +.. lua:attribute:: magneticAccuracy: table - The value can be one of the following: + Magnetic field accuracy constants. - - ``motion.magneticAccuracy.uncalibrated``: The magnetic field data is uncalibrated. - - ``motion.magneticAccuracy.low``: The magnetic field data is of low accuracy. - - ``motion.magneticAccuracy.medium``: The magnetic field data is of medium accuracy. - - ``motion.magneticAccuracy.high``: The magnetic field data is of high accuracy. + .. helptext:: magnetic field accuracy constants - .. helptext:: get the magnetic field accuracy + :param const uncalibrated: The magnetic field data is uncalibrated. + :param const low: The magnetic field data is of low accuracy. + :param const medium: The magnetic field data is of medium accuracy. + :param const high: The magnetic field data is of high accuracy. From 1efdf4247bbc3a851c9dd2a3ae1b6296e5b2aea9 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Tue, 28 Apr 2026 13:28:49 +0930 Subject: [PATCH 07/11] Further docs improvements --- docs/source/api/motion.rst | 7 +- docs/source/api/viewer.rst | 248 +++++++++++++++++++++++++++++++++++-- 2 files changed, 237 insertions(+), 18 deletions(-) diff --git a/docs/source/api/motion.rst b/docs/source/api/motion.rst index ca29638..fcc9a2d 100644 --- a/docs/source/api/motion.rst +++ b/docs/source/api/motion.rst @@ -1,13 +1,10 @@ -Motion Overview -=============== +Motion +====== Exposes Core Motion functionalities such as accessing the device's accelerometer, gyroscope, and magnetometer data. Tracking of motion metrics can impact performance and battery drain. Use this feature judiciously to avoid negatively affecting the user experience. -Motion -====== - .. lua:module:: motion .. lua:attribute:: autoStart: boolean diff --git a/docs/source/api/viewer.rst b/docs/source/api/viewer.rst index 246129e..2e94b0b 100644 --- a/docs/source/api/viewer.rst +++ b/docs/source/api/viewer.rst @@ -1,35 +1,257 @@ viewer ====== - -Exposes viewer properties common to both the legacy and modern runtimes. + +Controls the Codea viewer and exposes display mode, runtime, presentation, and screen layout state. .. lua:module:: viewer .. lua:attribute:: mode: enum - The display mode of the viewer. You can use this to render your games and simulations in fullscreen mode, fullscreen mode without buttons, or the standard mode. The standard mode includes a sidebar with your output and parameters, as well as buttons to control project execution. + Changes the display mode of the viewer. Use this to render your games and simulations in fullscreen mode, fullscreen mode without buttons, or standard mode. Standard mode includes the sidebar with output and parameters, plus controls for project execution. + + .. helptext:: current viewer display mode + + :syntax: + + .. code-block:: lua + + viewer.mode = STANDARD + viewer.mode = FULLSCREEN + viewer.mode = FULLSCREEN_NO_BUTTONS + +.. lua:attribute:: framerate: number + + Sets the preferred framerate of the viewer. You can set this to ``0``, ``15``, ``30``, ``60`` or ``120``. The value ``0`` uses the maximum framerate of your device. + + Note that this sets the preferred framerate. If the framerate cannot be maintained, it may drop below your preferred setting to the next lower value. + + .. helptext:: preferred viewer framerate + + :syntax: + + .. code-block:: lua + + viewer.framerate = 30 + +.. lua:attribute:: pointerLocked: boolean + + Setting this property to ``true`` indicates the renderer's preference to lock the pointer, although the system may not honor the request. For the system to consider locking the pointer, the viewer must be running fullscreen on your device. - .. helptext:: STANDARD or FULLSCREEN - - Values: + .. helptext:: whether pointer locking is desired - * ``STANDARD`` - * ``FULLSCREEN`` - * ``FULLSCREEN_NO_BUTTONS`` + :syntax: + + .. code-block:: lua + + viewer.pointerLocked = true + +.. lua:attribute:: runtime: number [readonly] + + The active runtime type, either ``LEGACY`` or ``MODERN``. + + .. helptext:: current runtime type + + :syntax: + + .. code-block:: lua + + if viewer.runtime == viewer.MODERN then + style.strokeWidth(5) + end .. lua:attribute:: safeArea: table - - A UIEdgeInsets object with the current safe area insets of the viewer, which can be accessed using ``viewer.safeArea.top``, ``viewer.safeArea.bottom``, ``viewer.safeArea.left``, and ``viewer.safeArea.right`` + + A table specifying the current safe area insets of the viewer. Use these values to avoid rendering important visible or interactive content under system interface areas. .. helptext:: safe area insets of the viewer - + :param number top: The top inset of the safe area. - :param number bottom: The bottom inset of the safe area. :param number left: The left inset of the safe area. + :param number bottom: The bottom inset of the safe area. :param number right: The right inset of the safe area. + :syntax: + + .. code-block:: lua + + print(viewer.safeArea.bottom) + +.. lua:attribute:: uniformResizing: boolean + + Controls whether the viewer preserves a uniform resizing behavior when the view changes size. This is only supported on platforms where the viewer is resizable. + + .. helptext:: use uniform viewer resizing + +.. lua:function:: resize(width, height) + + Resizes the viewer to the specified width and height. This is only supported on platforms where the viewer is resizable. + + .. helptext:: resize the viewer + + :param width: The new viewer width. + :type width: number + :param height: The new viewer height. + :type height: number + + :syntax: + + .. code-block:: lua + + viewer.resize(800, 600) + .. lua:attribute:: paused: boolean A boolean value that indicates whether the viewer is paused. .. helptext:: paused state of the viewer + +.. lua:attribute:: displayStats: boolean + + Controls whether runtime statistics are displayed in the viewer. + + .. helptext:: show viewer statistics + +.. lua:attribute:: drawOnRequest: boolean + + Controls whether the viewer draws only when a redraw is requested. + + .. helptext:: draw only when requested + +.. lua:attribute:: showWarnings: boolean + + Determines whether warnings should be displayed in the viewer. For example, warnings will be printed when using deprecated Codea APIs. + + .. helptext:: show viewer warnings + +.. lua:function:: close() + + Closes the viewer and returns to the editor. Calling ``viewer.close()`` is functionally the same as pressing the on-screen Back button. + + .. helptext:: close the viewer + + :syntax: + + .. code-block:: lua + + viewer.close() + +.. lua:function:: restart() + + Restarts the viewer, starting your project again. Calling ``viewer.restart()`` is functionally the same as pressing the on-screen Restart button. + + .. helptext:: restart the viewer + + :syntax: + + .. code-block:: lua + + viewer.restart() + +.. lua:function:: snapshot() + + Captures the rendered contents of the viewer and returns them as an ``image``. This captures the rendered scene and does not include the sidebar UI. + + .. helptext:: capture the viewer as an image + + :return: The rendered viewer contents. + :rtype: image + + :syntax: + + .. code-block:: lua + + local img = viewer.snapshot() + +.. lua:function:: alert(message[, title]) + + Shows a system alert. The ``message`` parameter specifies the message to display. The optional ``title`` parameter provides the title of the alert. If no title is specified, ``"Alert"`` is used. + + .. helptext:: show a system alert + + :param message: Message to display. + :type message: string + :param title: Alert title. + :type title: string + + :syntax: + + .. code-block:: lua + + viewer.alert("Hello World") + viewer.alert("Hello World", "Title") + +.. lua:function:: share(data) + + Shows a system share view for an image, string, or table of shareable items. This allows you to share content to a third-party service, save it to your device, or copy it to the pasteboard. + + .. helptext:: share viewer content + + :param data: Content to share. + :type data: image | string | table + + :syntax: + + .. code-block:: lua + + viewer.share(viewer.snapshot()) + +.. lua:attribute:: isPresenting: boolean [readonly] + + Returns whether the viewer is presenting an alert, share sheet, or another view that obscures the viewer. + + .. helptext:: whether the viewer is presenting another view + + :syntax: + + .. code-block:: lua + + if not viewer.isPresenting then + viewer.alert("Ready") + end + +.. lua:currentmodule:: None + +.. lua:attribute:: STANDARD: const + + Standard display mode. The output and parameters panes are visible, and the Back, Pause, Play and Reset buttons are shown. + + .. helptext:: standard viewer mode + + .. symbol:: const + :group: viewer-mode + +.. lua:attribute:: FULLSCREEN: const + + Fullscreen display mode. An exit fullscreen button remains visible. + + .. helptext:: fullscreen viewer mode + + .. symbol:: const + :group: viewer-mode + +.. lua:attribute:: FULLSCREEN_NO_BUTTONS: const + + Fullscreen display mode with all buttons hidden. Use ``viewer.close()`` if your project needs an explicit way to leave the viewer. + + .. helptext:: fullscreen viewer mode without buttons + + .. symbol:: const + :group: viewer-mode + +.. lua:attribute:: LEGACY: const + + Legacy runtime identifier. + + .. helptext:: legacy renderer + + .. symbol:: const + :group: viewer-type + +.. lua:attribute:: MODERN: const + + Modern runtime identifier. + + .. helptext:: modern renderer + + .. symbol:: const + :group: viewer-type From 6143f9bf437d3cf34fa156e9e5d82ea14a5aaae1 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Tue, 28 Apr 2026 13:34:51 +0930 Subject: [PATCH 08/11] Updates sound structure --- docs/source/api/sound.rst | 92 +++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/docs/source/api/sound.rst b/docs/source/api/sound.rst index e61d510..c0c5a5a 100644 --- a/docs/source/api/sound.rst +++ b/docs/source/api/sound.rst @@ -1,56 +1,10 @@ -sound +Sound ===== .. lua:module:: sound The sound module provides a way to play and manage sound effects and background music -.. lua:currentmodule:: None - -.. lua:attribute:: SOUND_COIN: const - - Procedural coin sound preset (SFXR) - - .. helptext:: coin sound effect constant - -.. lua:attribute:: SOUND_LASER: const - - Procedural laser sound preset (SFXR) - - .. helptext:: laser sound effect constant - -.. lua:attribute:: SOUND_EXPLOSION: const - - Procedural explosion sound preset (SFXR) - - .. helptext:: explosion sound effect constant - -.. lua:attribute:: SOUND_POWERUP: const - - Procedural powerup sound preset (SFXR) - - .. helptext:: power-up sound effect constant - -.. lua:attribute:: SOUND_HURT: const - - Procedural hurt sound preset (SFXR) - - .. helptext:: hurt sound effect constant - -.. lua:attribute:: SOUND_JUMP: const - - Procedural jump sound preset (SFXR) - - .. helptext:: jump sound effect constant - -.. lua:attribute:: SOUND_BLIP: const - - Procedural blip sound preset (SFXR) - - .. helptext:: blip sound effect constant - -.. lua:currentmodule:: sound - .. lua:staticmethod:: play(preset[, seed]) Plays a preset procedural SFXR sound effect using a given ``preset`` and optional ``seed`` @@ -182,3 +136,47 @@ The sound module provides a way to play and manage sound effects and background Stop the sound instance from playing .. helptext:: stop the sound + +.. lua:currentmodule:: None + +.. lua:attribute:: SOUND_COIN: const + + Procedural coin sound preset (SFXR) + + .. helptext:: coin sound effect constant + +.. lua:attribute:: SOUND_LASER: const + + Procedural laser sound preset (SFXR) + + .. helptext:: laser sound effect constant + +.. lua:attribute:: SOUND_EXPLOSION: const + + Procedural explosion sound preset (SFXR) + + .. helptext:: explosion sound effect constant + +.. lua:attribute:: SOUND_POWERUP: const + + Procedural powerup sound preset (SFXR) + + .. helptext:: power-up sound effect constant + +.. lua:attribute:: SOUND_HURT: const + + Procedural hurt sound preset (SFXR) + + .. helptext:: hurt sound effect constant + +.. lua:attribute:: SOUND_JUMP: const + + Procedural jump sound preset (SFXR) + + .. helptext:: jump sound effect constant + +.. lua:attribute:: SOUND_BLIP: const + + Procedural blip sound preset (SFXR) + + .. helptext:: blip sound effect constant \ No newline at end of file From 8fff44792ec1bc16f67bd0d0c979621f9d7b0291 Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Wed, 29 Apr 2026 00:08:28 +0930 Subject: [PATCH 09/11] More documentation cleaning --- README.md | 42 +++++++++++++++++++++++++++++++ docs/source/api/graphics.rst | 37 +++++++++++++++++++++++++-- docs/source/api/math_types.rst | 18 ++++++++++--- docs/source/builders/luadoc.py | 23 ++++++++++++++++- docs/source/builders/luastruct.py | 15 ++++++++++- 5 files changed, 127 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 53a1ffa..c23dc63 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,48 @@ autocomplete and type lookup, and `displayType` for Reference display. The private class is authored explicitly so function arguments and returned table fields can differ. +### Constructors + +Document constructors as a `lua:staticmethod` nested inside the class, using the +same callable name as the class. The `luadoc` builder treats same-name nested +staticmethods as constructors and emits `kind: "constructor"` in JSON, so Codea +can show constructor rows and infer constructor return types without implying a +`Type.Type()` API. + +```rst +.. lua:class:: mesh + + .. lua:staticmethod:: mesh([submeshCount]) + + Create an empty mesh. +``` + +For module-scoped and dotted classes, keep the constructor signature aligned +with the public call: + +```rst +.. lua:module:: physics2d + +.. lua:class:: world + + .. lua:staticmethod:: world() +``` + +```rst +.. lua:class:: gesture.tap + + .. lua:staticmethod:: gesture.tap(callback) +``` + +Rules: + +- Use same-name nested staticmethods only for constructors. +- Do not document a true static method with the same name as its type. +- Repeat the same constructor staticmethod with different signatures to document + overloads. +- Continue using ordinary `lua:staticmethod` entries for real static factories, + such as `mesh.sphere`, `image.cube`, and `shader.compute`. + ### Symbol Annotations Use `.. symbol::` for syntax-highlighting classifications. Symbol annotations diff --git a/docs/source/api/graphics.rst b/docs/source/api/graphics.rst index dce32b3..e4ca118 100644 --- a/docs/source/api/graphics.rst +++ b/docs/source/api/graphics.rst @@ -6,13 +6,32 @@ graphics commands Background ########## -.. lua:function:: background() +.. lua:function:: background(red, green, blue, alpha) Clears the current context with solid color, can also be used to set image backgrounds when combined with :lua:func:`context.push` - .. helptext:: set the background color, image or shader + :param red: The red component of the color (0-255) + :type red: number + :param green: The green component of the color (0-255) + :type green: number + :param blue: The blue component of the color (0-255) + :type blue: number + :param alpha: The alpha component of the color (0-255) + :type alpha: number + + .. helptext:: set the background color .. editor:: color +.. lua:function:: background(color) + + Clears the current context with solid color, can also be used to set image backgrounds when combined with :lua:func:`context.push` + + :param color: The color to set the background to + :type color: color + + .. helptext:: set the background color + .. editor:: color + .. lua:function:: background(cubeImage, [mipLevel = 0]) Clears the current background with the contents of a cube image, using the current camera settings to define eye direction @@ -20,12 +39,17 @@ Background .. helptext:: clear the background with a cube image :param cubeImage: The image to clear the background with + :type cubeImage: image :param mipLevel: The mip level of the image to use, useful for displaying pre-blurred image mips, such as those calculated using :lua:meth:`image.generateIrradiance` + :type mipLevel: number .. lua:function:: background(shader) Clears the current background using a custom shader + :param shader: The shader to use when clearing the background, should be a shader that is compatible with sprite rendering (i.e. uses the same vertex attributes) + :type shader: shader + .. helptext:: clear the background with a shader .. collapse:: Example @@ -42,6 +66,15 @@ A set of graphics functions which are so commonly used they are in the global na Draws 2D line from the start point (x1, y1) to the end point (x2, y2) based on the current style: + :param x1: the x coordinate of the start point + :type x1: number + :param y1: the y coordinate of the start point + :type y1: number + :param x2: the x coordinate of the end point + :type x2: number + :param y2: the y coordinate of the end point + :type y2: number + - *Color* with :lua:func:`style.stroke` - *Width* with :lua:func:`style.strokeWidth` - *End Caps* with :lua:func:`style.lineCapMode` diff --git a/docs/source/api/math_types.rst b/docs/source/api/math_types.rst index 17bdf2d..910b2a8 100644 --- a/docs/source/api/math_types.rst +++ b/docs/source/api/math_types.rst @@ -219,12 +219,11 @@ Math .. lua:class:: vec3 - .. lua:staticmethod:: vec3(x) - vec3(x, y, z) + .. lua:staticmethod:: vec3(x, y, z) - Create a new ``vec3`` by setting all components to the same value, or each one individually + Create a new ``vec3`` by setting each component individually - :param x: The x component (also used for y and z when called with a single argument) + :param x: The x component :type x: number :param y: The y component :type y: number @@ -233,6 +232,17 @@ Math .. helptext:: create a new vec3 + .. lua:staticmethod:: vec3() + + Create a new ``vec3`` by setting all components to zero + + .. lua:staticmethod:: vec3(v) + + Create a new ``vec3`` by setting all components to the same value + + :param v: The x, y and z values + :type v: number + .. lua:staticmethod:: min(v1, v2) Return a ``vec3`` containing the component-wise minimum of two vectors diff --git a/docs/source/builders/luadoc.py b/docs/source/builders/luadoc.py index c9375f9..498ec91 100644 --- a/docs/source/builders/luadoc.py +++ b/docs/source/builders/luadoc.py @@ -208,7 +208,20 @@ def unknown_visit(self, node): self.add_to_current_scope(LuaAttribute(node, objtype, self.current_group, symbol=symbol)) elif objtype == 'staticmethod': - method = LuaFunction(node, 'staticmethod', self.current_group, symbol) + if self.is_constructor_node(node): + current_class = self.class_stack[-1] + parsed = LuaFunction(node, 'constructor', self.current_group, symbol) + method = LuaFunction( + node, + 'constructor', + self.current_group, + symbol, + name=current_class.name, + module=current_class.module, + returns=parsed.returns or [LuaReturn(type_hint=current_class.full_name())] + ) + else: + method = LuaFunction(node, 'staticmethod', self.current_group, symbol) self.add_to_current_scope(method) def unknown_departure(self, node): @@ -230,6 +243,14 @@ def add_to_current_scope(self, element): else: self.entries.append(element) + def is_constructor_node(self, node): + if not self.class_stack: + return False + + method_name = DocutilsUtils.extract_name(node) + class_name = self.class_stack[-1].name + return method_name == class_name.split('.')[-1] + def setup(app): app.add_builder(LuaJSONBuilder) diff --git a/docs/source/builders/luastruct.py b/docs/source/builders/luastruct.py index f68f07b..cceaf88 100644 --- a/docs/source/builders/luastruct.py +++ b/docs/source/builders/luastruct.py @@ -1,6 +1,8 @@ from docutils import nodes from enum import Enum +_UNSET = object() + class DocutilsUtils: @staticmethod def markdown_text(node): @@ -324,6 +326,11 @@ def __init__(self, node=None, group=None, name=None, description=None, module=No def __str__(self): return f"{self.name} [{self.module}]\n\t{self.description}" + def full_name(self): + if self.module: + return f"{self.module}.{self.name}" + return self.name + def to_dict(self): d = { 'name': self.name, @@ -386,7 +393,7 @@ def to_dict(self): return d class LuaFunction: - def __init__(self, node, type, group=None, symbol=None): + def __init__(self, node, type, group=None, symbol=None, name=_UNSET, module=_UNSET, returns=None): self.name = DocutilsUtils.extract_name(node) self.module = DocutilsUtils.extract_module(node) self.description = DocutilsUtils.extract_description(node) @@ -400,6 +407,12 @@ def __init__(self, node, type, group=None, symbol=None): self.symbol = DocutilsUtils.extract_symbol(node) or symbol self.returns = self.extract_returns(node) self.type = type + if name is not _UNSET: + self.name = name + if module is not _UNSET: + self.module = module + if returns is not None: + self.returns = returns def parse_return_type(self, type_text): if '$' not in type_text: From af481ad3d7d4c8d6a9f845e7caf41f93581fdedb Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Wed, 29 Apr 2026 13:45:35 +0930 Subject: [PATCH 10/11] Better argument parsing in luadoc builder --- docs/source/builders/luadomain.py | 39 ++++++++++++++++++++++++++++--- docs/source/builders/luastruct.py | 18 +++++++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/docs/source/builders/luadomain.py b/docs/source/builders/luadomain.py index ebaaef4..8013f9f 100644 --- a/docs/source/builders/luadomain.py +++ b/docs/source/builders/luadomain.py @@ -39,17 +39,50 @@ ''', re.VERBOSE) +def _split_top_level_args(arg_list: str) -> List[str]: + arguments = [] + start = 0 + stack = [] + quote = None + escaped = False + pairs = {')': '(', '}': '{'} + + for index, char in enumerate(arg_list): + if quote: + if escaped: + escaped = False + elif char == '\\': + escaped = True + elif char == quote: + quote = None + continue + + if char in ('"', "'"): + quote = char + elif char in '({': + stack.append(char) + elif char in pairs: + if stack and stack[-1] == pairs[char]: + stack.pop() + elif char == ',' and not stack: + arguments.append(arg_list[start:index].strip()) + start = index + 1 + + arguments.append(arg_list[start:].strip()) + return arguments + + def _pseudo_parse_arglist(sig_node: addnodes.desc_signature, arg_list: str) -> None: """"Parse" a list of arguments separated by commas. Arguments can have "optional" annotations given by enclosing them in - brackets. Currently, this will split at any comma, even if it's inside a - string literal (e.g. default argument value). + brackets. Commas inside nested delimiters or quoted strings are preserved, + so defaults like ``vec3(1, 1, 1)`` remain part of one argument. """ param_list = addnodes.desc_parameterlist() stack = [param_list] try: - for argument in arg_list.split(','): + for argument in _split_top_level_args(arg_list): argument = argument.strip() ends_open = ends_close = 0 while argument.startswith('['): diff --git a/docs/source/builders/luastruct.py b/docs/source/builders/luastruct.py index cceaf88..4bbb01e 100644 --- a/docs/source/builders/luastruct.py +++ b/docs/source/builders/luastruct.py @@ -132,8 +132,10 @@ def extract_parameters(node, isClass = False): for param_description_nodes in param_paragraphs: param_name_node = param_description_nodes.next_node(condition=lambda n: n.tagname == 'literal_strong') if param_name_node and param_description_nodes: - param_name = param_name_node.astext().split('=')[0].strip() - default_value = param_name_node.astext().split('=')[1].strip() if '=' in param_name_node.astext() else None + param_text = param_name_node.astext() + param_parts = param_text.split('=', 1) + param_name = param_parts[0].strip() + default_value = param_parts[1].strip() if len(param_parts) > 1 else None param_type = None description_text = param_description_nodes.astext() @@ -154,8 +156,10 @@ def extract_parameters(node, isClass = False): for child in param_list.children: if child.tagname == 'desc_parameter' or child.tagname == 'desc_optional': for param_node in child.children: - param_name = param_node.astext().split('=')[0].strip() - default_value = param_node.astext().split('=')[1].strip() if '=' in param_node.astext() else None + param_text = param_node.astext() + param_parts = param_text.split('=', 1) + param_name = param_parts[0].strip() + default_value = param_parts[1].strip() if len(param_parts) > 1 else None optional = child.tagname == 'desc_optional' param_info = param_details.get(param_name, {}) params.append(LuaParameter(name=param_name, @@ -548,10 +552,12 @@ def extract_fields(self, node): if param_name_node and param_description_node: # Extract the parameter name - param_name = param_name_node.astext().split('=')[0].strip() + param_text = param_name_node.astext() + param_parts = param_text.split('=', 1) + param_name = param_parts[0].strip() # Handle default values if specified - default_value = param_name_node.astext().split('=')[1].strip() if '=' in param_name_node.astext() else None + default_value = param_parts[1].strip() if len(param_parts) > 1 else None # Extract the type from the description if available within parenthesis param_type = None From ffba75b8e8fee961766237cc99a50915101af95c Mon Sep 17 00:00:00 2001 From: Sim Saens Date: Wed, 29 Apr 2026 21:47:38 +0930 Subject: [PATCH 11/11] Improved graphics and style docs --- docs/source/api/graphics.rst | 49 +++++++++++- docs/source/api/style.rst | 142 +++++++++++++++++++++++++++++++---- 2 files changed, 172 insertions(+), 19 deletions(-) diff --git a/docs/source/api/graphics.rst b/docs/source/api/graphics.rst index e4ca118..e323bb7 100644 --- a/docs/source/api/graphics.rst +++ b/docs/source/api/graphics.rst @@ -145,11 +145,17 @@ A set of graphics functions which are so commonly used they are in the global na Draws a 2D arc with a given origin, radius and start, end angles + direction :param x: x coordinate of the arc origin + :type x: number :param y: y coordinate of the arc origin + :type y: number :param radius: the radius arc + :type radius: number :param startAngle: the start angle of the arc (in degrees) + :type startAngle: number :param endAngle: the end angle of the arc (in degrees) + :type endAngle: number :param dir: the direction of the arc, 1 or clockwise, -1 for anti-clockwise + :type dir: integer .. helptext:: draw a 2D arc @@ -160,16 +166,51 @@ A set of graphics functions which are so commonly used they are in the global na .. helptext:: draw a circle or oval -.. lua:function:: rect(x, y, w, h) - rect(x, y, w, h, r) - rect(x, y, w, h, r1, r2, r3, r4) +.. lua:function:: rect(x, y, w, h, [radius = 0]) Draws a rectangle with a given origin point and width / height, origin and sizing behaviour depends on :lua:func:`style.shapeMode` - Additional arguments allow for rounded corners (either all one radius or four separate radii) + Optional parameter ``radius`` specified the corner radius + + :param x: the x coordinate of the rectangle + :type x: number + :param y: the y coordinate of the rectangle + :type y: number + :param w: the width of the rectangle + :type w: number + :param h: the height of the rectangle + :type h: number + :param radius: the radius of the rounded corners + :type radius: number .. helptext:: draw a rectangle +.. lua:function:: rect(x, y, w, h, r1, r2, r3, r4) + + Draws a rectangle with a given origin point and width / height, origin and sizing behaviour depends on :lua:func:`style.shapeMode` + + The corner radius of each corner can be set independently using the additional parameters r1, r2, r3 and r4 + + :param x: the x coordinate of the rectangle + :type x: number + :param y: the y coordinate of the rectangle + :type y: number + :param w: the width of the rectangle + :type w: number + :param h: the height of the rectangle + :type h: number + :param r1: the radius of the top-left corner + :type r1: number + :param r2: the radius of the top-right corner + :type r2: number + :param r3: the radius of the bottom-right corner + :type r3: number + :param r4: the radius of the bottom-left corner + :type r4: number + + .. helptext:: draw a rectangle with rounded corners + + Sprites ####### diff --git a/docs/source/api/style.rst b/docs/source/api/style.rst index ede0da9..24fa34c 100644 --- a/docs/source/api/style.rst +++ b/docs/source/api/style.rst @@ -14,24 +14,36 @@ General Push the current style onto the stack + :returns: The style table, for chaining. + :rtype: style + .. helptext:: push the current style onto the stack .. lua:function:: push(style) Push a specific style onto the stack + :returns: The style table, for chaining. + :rtype: style + .. helptext:: push a style onto the stack .. lua:function:: pop() Pop the current style from the stack, restoring the previous style + :returns: The style table, for chaining. + :rtype: style + .. helptext:: pop the current style from the stack .. lua:function:: reset() Reset the current style to defaults. Use this to restore the default style + :returns: The style table, for chaining. + :rtype: style + .. helptext:: reset the style to defaults .. lua:function:: get() @@ -44,43 +56,57 @@ General Sets the current style from a graphicsStyle object + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the current style -.. lua:function:: fill() - fill() -> r, g, b, a +.. lua:function:: fill(color) - Sets/gets the fill color for use in vector drawing operations + Sets the fill color for use in vector drawing operations .. helptext:: set the fill color .. editor:: color -.. lua:function:: noFill() + :returns: The style table, for chaining. + :rtype: style - Disables fill +.. lua:function:: fill() -> r, g, b, a - .. helptext:: clear the fill color + Gets the current fill color for use in vector drawing operations -.. lua:function:: stroke() + .. helptext:: get the fill color + .. editor:: color - Gets the current stroke color for use in vector drawing operations +.. lua:function:: noFill() - .. helptext:: get the stroke color - .. editor:: color + Disables fill + + :returns: The style table, for chaining. + :rtype: style + + .. helptext:: clear the fill color .. lua:function:: stroke(color) Sets the stroke color to the specified color, or a grayscale value + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the stroke color .. editor:: color :param color: The color to set the stroke to, or a grayscale value - :type color: color or number + :type color: color or number .. lua:function:: stroke(gray, alpha) Sets the stroke color to the specified grayscale value and alpha + :returns: The style table, for chaining. + :rtype: style + :param number gray: The grayscale value to set the stroke to :param number alpha: The alpha value to set the stroke to @@ -91,6 +117,9 @@ General Sets the stroke color to the specified red, green, and blue values + :returns: The style table, for chaining. + :rtype: style + :param number red: The red value to set the stroke to :param number green: The green value to set the stroke to :param number blue: The blue value to set the stroke to @@ -102,6 +131,9 @@ General Sets the stroke color to the specified red, green, blue, and alpha values + :returns: The style table, for chaining. + :rtype: style + :param number red: The red value to set the stroke to :param number green: The green value to set the stroke to :param number blue: The blue value to set the stroke to @@ -110,16 +142,29 @@ General .. helptext:: set the stroke color .. editor:: color +.. lua:function:: stroke() + + Gets the current stroke color for use in vector drawing operations + + .. helptext:: get the stroke color + .. editor:: color + .. lua:function:: noStroke() Disables stroke + :returns: The style table, for chaining. + :rtype: style + .. helptext:: clear the stroke color .. lua:function:: tint() Sets the tint color for use with :lua:func:`sprite` and :lua:meth:`mesh.draw` + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the tint color for images drawn with sprite() .. editor:: color @@ -134,6 +179,9 @@ General Sets/gets the scale of a sprite when rendering + :returns: The style table, for chaining. + :rtype: style + :param number scale: the scaling factor of the sprite image .. helptext:: set the pixel scaling for sprite() @@ -142,6 +190,9 @@ General Sets the stroke width for use in vector drawing operations + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the width of outlines .. lua:function:: strokeWidth() -> number @@ -154,6 +205,9 @@ General Sets the current line cap mode, used by :lua:`line`, :lua:`polyline` and :lua:`shape` + :returns: The style table, for chaining. + :rtype: style + - :lua:attr:`ROUND` - :lua:attr:`SQUARE` - :lua:attr:`PROJECT` @@ -170,6 +224,9 @@ General Sets the current line join mode, used by :lua:`polyline`, :lua:`polygon` and :lua:`shape` when joining multiple line segments + :returns: The style table, for chaining. + :rtype: style + - :lua:attr:`ROUND` - :lua:attr:`MITER` - :lua:attr:`BEVEL` @@ -183,9 +240,11 @@ General .. helptext:: get the current line join style .. lua:function:: shapeMode(mode) - shapeMode() -> enum - Sets/gets the current shape mode, used by :lua:`rect`, :lua:`ellipse` and :lua:`sprite` + Sets the current shape mode, used by :lua:`rect`, :lua:`ellipse` and :lua:`sprite` + + :returns: The style table, for chaining. + :rtype: style - :lua:attr:`CENTER` - Draw shapes from the center and size using width/height - :lua:attr:`CORNERS` - Draw shapes by specifying the two opposite corners @@ -194,6 +253,12 @@ General .. helptext:: set the drawing origin for rect() and ellipse() +.. lua:function:: shapeMode() -> enum + + Gets the current shape mode + + .. helptext:: get the drawing origin for rect() and ellipse() + Constants - Shape Mode ********************** @@ -222,6 +287,9 @@ Constants - Shape Mode .. lua:function:: sortOrder(order) + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the sort order for drawing Blending Style @@ -234,6 +302,9 @@ Functions Sets the current blend mode to one of the available presets. Blending composites pixels onto the current drawing context based on source and destination color and alpha values + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the blend mode for drawing The default mode is :lua:`NORMAL` which applies standard alpha blended transparency with the following equation: @@ -247,12 +318,18 @@ Functions Sets a custom blend mode for both rgb and alpha components using ``src`` (source) and ``dst`` destination blending factors + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set a custom blend mode .. lua:function:: blend(src, dst, srcAlpha, dstAlpha) Sets a custom blend mode with separate blending factors for both rgb and alpha components + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set a custom blend mode with separate alpha factors .. lua:function:: blend() -> src, dst, srcAlpha, dstAlpha @@ -265,6 +342,9 @@ Functions Sets the same blend function for both rgb and alpha components (the default is :lua:`EQUATION_ADD`) which determines how source and destination parts of the blending equation are combined + :returns: The style table, for chaining. + :rtype: style + - :lua:`EQUATION_ADD` - Add (default) :math:`R = R_s*k_s+R_d*k_d` - :lua:`EQUATION_SUB` - Subtract @@ -282,6 +362,9 @@ Functions Sets separate blend functions for rgb and alpha components + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set separate blend equation functions for rgb and alpha .. lua:function:: blendFunc() -> func, alphaFunc @@ -472,6 +555,9 @@ Viewport Sets the viewport of the renderer + :returns: The style table, for chaining. + :rtype: style + :param number x: The x position of the viewport :param number y: The y position of the viewport :param number w: The width of the viewport @@ -488,28 +574,45 @@ Clipping *Note: the clipping rectangle is effected by the current matrix transform* + :returns: The style table, for chaining. + :rtype: style + .. helptext:: setup a clipping region on the screen .. lua:function:: noClip() Disables clipping + :returns: The style table, for chaining. + :rtype: style + .. helptext:: disable clipping Stencil ####### .. lua:function:: stencil(state) - stencil() - Sets/gets the current stencil state for both front and back faces + Sets the current stencil state for both front and back faces + + :returns: The style table, for chaining. + :rtype: style .. helptext:: set the stencil state for front and back faces +.. lua:function:: stencil() + + Gets the current stencil state for both front and back faces + + .. helptext:: get the stencil state for front and back faces + .. lua:function:: stencil(front, back) Sets the current stencil state for front and back faces separately + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set separate stencil states for front and back faces Using Stencils @@ -620,14 +723,23 @@ Text Style .. lua:function:: fontSize(size) + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the font size for text() .. lua:function:: textAlign(align) + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set alignment for text() .. lua:function:: textStyle(style) + :returns: The style table, for chaining. + :rtype: style + .. helptext:: set the style for text() Constants - Text