Merge: gamnit: add documentation and standardize the API
authorJean Privat <jean@pryen.org>
Wed, 17 May 2017 13:53:42 +0000 (09:53 -0400)
committerJean Privat <jean@pryen.org>
Wed, 17 May 2017 13:53:42 +0000 (09:53 -0400)
Improve the documentation for the central services of gamnit, mainly for entities visible from gamnit.org. Also, to improve the API, remove the `Gamnit` from some class name and move a few named constuctors to factories or their own class.

Pull-Request: #2422
Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr>
Reviewed-by: Jean Privat <jean@pryen.org>

contrib/model_viewer/src/globe.nit
contrib/model_viewer/src/model_viewer.nit
lib/core/collection/abstract_collection.nit
lib/gamnit/depth/depth_core.nit
lib/gamnit/depth/more_materials.nit
lib/gamnit/depth/more_meshes.nit
lib/gamnit/depth/more_models.nit
lib/gamnit/depth/selection.nit
lib/gamnit/flat.nit
lib/gamnit/textures.nit
lib/geometry/points_and_lines.nit

index 5ad0407..8fe1158 100644 (file)
@@ -63,13 +63,13 @@ class GlobeModel
        redef fun load
        do
                leaves.add new LeafModel(
-                       new Mesh.uv_sphere(1.0, 2*n_parallels, n_parallels),
+                       new UVSphere(1.0, 2*n_parallels, n_parallels),
                        new GlobeMaterial.surface)
                leaves.add new LeafModel(
-                       new Mesh.uv_sphere(1.1, 2*n_parallels, n_parallels),
+                       new UVSphere(1.1, 2*n_parallels, n_parallels),
                        new GlobeMaterial.clouds)
                leaves.add new LeafModel(
-                       new Mesh.uv_sphere(1.2, 2*n_parallels, n_parallels),
+                       new UVSphere(1.2, 2*n_parallels, n_parallels),
                        new GlobeMaterial.atmo)
        end
 end
index a79fdf9..9353ef5 100644 (file)
@@ -30,9 +30,9 @@ redef class App
 
        # All available models
        var models: Array[Model] = [
-               new LeafModel(new Cube, new SmoothMaterial.default),
-               new LeafModel(new Mesh.uv_sphere(4.0, 32, 16), new SmoothMaterial.default),
-               new LeafModel(new Mesh.uv_sphere(4.0, 32, 16), new NormalsMaterial),
+               new LeafModel(new Cube, new Material),
+               new LeafModel(new UVSphere(4.0, 32, 16), new Material),
+               new LeafModel(new UVSphere(4.0, 32, 16), new NormalsMaterial),
                new Model("models/Tree_01.obj"),
                new Model("models/Oak_Fall_01.obj"),
                new Model("models/Quandtum_BA-2_v1_1.obj"),
index 5ed911f..309d048 100644 (file)
@@ -385,7 +385,7 @@ end
 interface SimpleCollection[E]
        super RemovableCollection[E]
 
-       # Add an item in a collection.
+       # Add `item` to this collection.
        #
        #     var a = [1,2]
        #     a.add 3
index 45863dc..994e8be 100644 (file)
@@ -17,10 +17,30 @@ module depth_core
 
 intrude import gamnit::flat
 
-# Visible entity in the game world, represented by its `model` modified by the other attributes
+# Visible 3D entity in the game world
+#
+# Similar to `gamnit::Sprite` which is in 2D.
+#
+# Each actor associates a `model` to the position `center`.
+# The appearance is modified by `rotation`, `scale` and `alpha`,
+# as well as the attributes of `model` itself.
+#
+# ~~~
+# import gamnit::depth
+#
+# # Load model from the assets folder
+# var model = new Model("path/in/assets.obj")
+#
+# # Create and configure an actor
+# var actor = new Actor(model, new Point3d[Float](0.0, 0.0, 0.0))
+# actor.scale = 2.0
+#
+# # Add to the visible game world
+# app.actors.add actor
+# ~~~
 class Actor
 
-       # Model used to dray this actor
+       # Model used to draw this actor
        var model: Model
 
        # Position of this sprite in world coordinates
@@ -29,14 +49,35 @@ class Actor
        # Rotation on the Z axis
        var rotation = 0.0 is writable
 
-       # Scale applied to this sprite
+       # Scale applied to the model
        var scale = 1.0 is writable
 
-       # Transparency applied to the texture on draw
+       # Transparency applied to the model on draw
+       #
+       # This value may be ignored by some materials.
+       # Non-opaque values may result in artifacts as there is no specialized
+       # support for transparent models and the depth buffer.
        var alpha = 1.0 is writable
 end
 
-# Entire 3D model defined by its `leaves`, an association of `Mesh` to `Material`
+# 3D model composed of `Mesh` and `Material`, loaded from the assets folder by default
+#
+# Instances can be created at any time and must be loaded after or at the end of `on_create`.
+# If loading fails, the model is replaced by `placeholder_model`.
+#
+# ~~~
+# import gamnit::depth
+#
+# var model = new Model("path/in/assets.obj")
+# model.load
+# ~~~
+#
+# The most simple model is `LeafModel`, composed of a single `Mesh` and `Material`.
+# It can be easily created programmatically to display simple geometries.
+# Whereas `CompositeModel` is composed of one or many `LeafModel` and is usually
+# loaded from the assets folder as a `ModelAsset`.
+# Instances of `ModelAsset` must be in the format OBJ and MAT,
+# and their texture in PNG or JPG.
 abstract class Model
 
        # Load this model in memory
@@ -56,7 +97,7 @@ class CompositeModel
        redef var leaves = new Array[LeafModel]
 end
 
-# Single model with a `mesh` and `material`
+# Basic model with a single `mesh` and `material`
 #
 # Only leaves are actually drawn by the `material`.
 class LeafModel
@@ -71,10 +112,28 @@ class LeafModel
        redef var leaves = [self]
 end
 
-# Material for a model or how to draw the model
+# Material for models, or how to draw the model
+#
+# To create a simple basic blueish material, use `new Material`.
+#
+# Each class of material is associated to a `GLProgram` and its GPU shaders.
+# The simple material `SmoothMaterial` allows to set an ambient, diffuse and specular color.
+# To which `TextureMaterial` adds three textures, for each kind of light.
+# The `NormalsMaterial` may be useful for debugging, it show the orientation of
+# the normal vectors as colors.
+#
+# ~~~
+# import gamnit::depth
+#
+# var blueish_material = new Material
+# var redish_material = new SmoothMaterial([0.3, 0.0, 0.0],
+#                                          [0.6, 0.0, 0.0],
+#                                          [1.0, 1.0, 1.0])
+# var normals_material = new NormalsMaterial
+# ~~~
 abstract class Material
 
-       # Draw `actor`
+       # Draw a `model` from `actor`
        #
        # This method should be refined by subclasses as the default implementation is a no-op.
        #
@@ -85,6 +144,17 @@ abstract class Material
 end
 
 # Mesh with all geometry data
+#
+# May be created via `Plane`, `Cube` or `UVSphere`,
+# or loaded from the assets folder indirectly with a `Model`.
+#
+# ~~~
+# import gamnit::depth
+#
+# var plane = new Plane
+# var cube = new Cube
+# var sphere = new UVSphere(1.0, 32, 16)
+# ~~~
 class Mesh
 
        # Vertices coordinates
@@ -105,58 +175,6 @@ class Mesh
 
        # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
        fun draw_mode: GLDrawMode do return gl_TRIANGLES
-
-       # Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
-       init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
-       do
-               var w = n_meridians
-               var h = n_parallels
-
-               var vertices = new Array[Float].with_capacity(w*h*3)
-               self.vertices = vertices
-
-               var texture_coords = new Array[Float].with_capacity(w*h*2)
-               self.texture_coords = texture_coords
-
-               var normals = new Array[Float].with_capacity(w*h*3)
-               self.normals = normals
-
-               # Build vertices
-               for m in [0..w[ do
-                       for p in [0..h[ do
-                               var u = m.to_f * 2.0 * pi / (w-1).to_f
-                               var v = p.to_f * pi / (h-1).to_f
-
-                               vertices.add radius * u.cos * v.sin
-                               vertices.add radius * v.cos
-                               vertices.add radius * u.sin * v.sin
-
-                               texture_coords.add (1.0 - m.to_f/(w-1).to_f)
-                               texture_coords.add(p.to_f/(h-1).to_f)
-
-                               normals.add u.cos * v.sin
-                               normals.add v.cos
-                               normals.add u.sin * v.sin
-                       end
-               end
-
-               # Build faces
-               var indices = new Array[Int].with_capacity((w-1)*(h-1)*6)
-               self.indices = indices
-               for m in [0..w-1[ do
-                       for p in [0..h-1[ do
-                               var a = m*h + p
-
-                               indices.add a
-                               indices.add a+h
-                               indices.add a+1
-
-                               indices.add a+h
-                               indices.add a+h+1
-                               indices.add a+1
-                       end
-               end
-       end
 end
 
 # Source of light
index b77c85e..4cc8dcb 100644 (file)
@@ -18,15 +18,17 @@ module more_materials
 intrude import depth_core
 intrude import flat
 
-# Simple material with static colors used for debugging or display abstract objects
-class SmoothMaterial
-       super Material
-
+redef class Material
        # Get the default blueish material
-       init default do init(
+       new do return new SmoothMaterial(
                [0.0, 0.0, 0.3, 1.0],
                [0.0, 0.0, 0.6, 1.0],
                [1.0, 1.0, 1.0, 1.0])
+end
+
+# Simple material with static colors
+class SmoothMaterial
+       super Material
 
        # Ambient color, always visible
        var ambient_color: Array[Float] is writable
@@ -144,7 +146,7 @@ class TexturedMaterial
                # If using a texture, set `texture_coords`
                program.tex_coord.array_enabled = sample_used_texture != null
                if sample_used_texture != null then
-                       if sample_used_texture isa GamnitRootTexture then
+                       if sample_used_texture isa RootTexture then
                                # Coordinates are directly valid
                                program.tex_coord.array(mesh.texture_coords, 2)
                        else
index eeeb258..0a4d53c 100644 (file)
@@ -80,6 +80,8 @@ class Plane
 end
 
 # Cube, with 6 faces
+#
+# Occupies `[-0.5..0.5]` on all three axes.
 class Cube
        super Mesh
 
@@ -132,3 +134,68 @@ class Cube
 
        redef var center = new Point3d[Float](0.0, 0.0, 0.0) is lazy
 end
+
+# Sphere with `radius` and a number of faces set by `n_meridians` and `n_parallels`
+class UVSphere
+       super Mesh
+
+       # Distance between the center and the vertices
+       var radius: Float
+
+       # Number of vertices on a full circle around the Z axis
+       var n_meridians: Int
+
+       # Number of vertices on an arc between both poles
+       var n_parallels: Int
+
+       init
+       do
+               var w = n_meridians
+               var h = n_parallels
+
+               var vertices = new Array[Float].with_capacity(w*h*3)
+               self.vertices = vertices
+
+               var texture_coords = new Array[Float].with_capacity(w*h*2)
+               self.texture_coords = texture_coords
+
+               var normals = new Array[Float].with_capacity(w*h*3)
+               self.normals = normals
+
+               # Build vertices
+               for m in [0..w[ do
+                       for p in [0..h[ do
+                               var u = m.to_f * 2.0 * pi / (w-1).to_f
+                               var v = p.to_f * pi / (h-1).to_f
+
+                               vertices.add radius * u.cos * v.sin
+                               vertices.add radius * v.cos
+                               vertices.add radius * u.sin * v.sin
+
+                               texture_coords.add (1.0 - m.to_f/(w-1).to_f)
+                               texture_coords.add(p.to_f/(h-1).to_f)
+
+                               normals.add u.cos * v.sin
+                               normals.add v.cos
+                               normals.add u.sin * v.sin
+                       end
+               end
+
+               # Build faces
+               var indices = new Array[Int].with_capacity((w-1)*(h-1)*6)
+               self.indices = indices
+               for m in [0..w-1[ do
+                       for p in [0..h-1[ do
+                               var a = m*h + p
+
+                               indices.add a
+                               indices.add a+h
+                               indices.add a+1
+
+                               indices.add a+h
+                               indices.add a+h+1
+                               indices.add a+1
+                       end
+               end
+       end
+end
index c2b9664..8d7d8a1 100644 (file)
@@ -62,7 +62,7 @@ class ModelAsset
                var content = text_asset.to_s
                if content.is_empty then
                        print_error "Model failed to load: Asset empty at '{self.path}'"
-                       leaves.add new LeafModel(new Cube, new SmoothMaterial.default)
+                       leaves.add new LeafModel(new Cube, new Material)
                        return
                end
 
@@ -71,7 +71,7 @@ class ModelAsset
                var obj_def = parser.parse
                if obj_def == null then
                        print_error "Model failed to load: .obj format error on '{self.path}'"
-                       leaves.add new LeafModel(new Cube, new SmoothMaterial.default)
+                       leaves.add new LeafModel(new Cube, new Material)
                        return
                end
 
@@ -212,7 +212,7 @@ private class ModelFromObj
                # Create models and store them
                for mesh, mtl_def in mesh_to_mtl do
                        var material = materials.get_or_null(mtl_def)
-                       if material == null then material = new SmoothMaterial.default
+                       if material == null then material = new Material
 
                        var model = new LeafModel(mesh, material)
                        array.add model
@@ -387,5 +387,5 @@ redef class Sys
        # Blue cube of 1 unit on each side, acting as placeholder for models failing to load
        #
        # This model can be freely used by any `Actor` as placeholder or for debugging.
-       var placeholder_model = new LeafModel(new Cube, new SmoothMaterial.default) is lazy
+       var placeholder_model = new LeafModel(new Cube, new Material) is lazy
 end
index 18744ee..3d0e841 100644 (file)
@@ -210,7 +210,7 @@ redef class TexturedMaterial
                # If using a texture, set `texture_coords`
                program.tex_coord.array_enabled = sample_used_texture != null
                if sample_used_texture != null then
-                       if sample_used_texture isa GamnitRootTexture then
+                       if sample_used_texture isa RootTexture then
                                # Coordinates are directly valid
                                program.tex_coord.array(mesh.texture_coords, 2)
                        else
index aef21d8..cbcc2e7 100644 (file)
@@ -46,11 +46,53 @@ import gamnit::dynamic_resolution
 import gamnit::limit_fps
 import gamnit::camera_control
 
-# Draw a `texture` at `center`
+# Visible 2D entity in the game world or UI
 #
-# An instance of `Sprite` can only belong to a single `SpriteSet` at
-# a time. The on screen position depends on the camera associated
+# Similar to `gamnit::Actor` which is in 3D.
+#
+# Each sprite associates a `texture` to the position `center`.
+# The appearance is modified by `rotation`, `invert_x`,
+# `scale`, `red`, `green`, `blue` and `alpha`.
+# These values can be changed at any time and will trigger an update
+# of the data on the GPU side, having a small performance cost.
+#
+# For a sprite to be visible, it must be added to either the world `sprites`
+# or the `ui_sprites`.
+# However, an instance of `Sprite` can only belong to a single `SpriteSet`
+# at a time. The final on-screen position depends on the camera associated
 # to the `SpriteSet`.
+#
+# ~~~
+# # Load texture and create sprite
+# var texture = new Texture("path/in/assets.png")
+# var sprite = new Sprite(texture, new Point3d[Float](0.0, 0.0, 0.0))
+#
+# # Add sprite to the visible game world
+# app.sprites.add sprite
+#
+# # Extra configuration of the sprite
+# sprite.rotation = pi/2.0
+# sprite.scale = 2.0
+#
+# # Show only the blue colors
+# sprite.red = 0.0
+# sprite.green = 0.0
+# ~~~
+#
+# To add a sprite to the UI it can be anchored to screen borders
+# with `ui_camera.top_left` and the likes.
+#
+# ~~~nitish
+# # Place it a bit off the top left of the screen
+# var pos = app.ui_camera.top_left.offset(128.0, -128.0, 0)
+#
+# # Load texture and create sprite
+# var texture = new Texture("path/in/assets.png")
+# var sprite = new Sprite(texture, pos)
+#
+# # Add it to the UI (above world sprites)
+# app.ui_sprites.add sprite
+# ~~~
 class Sprite
 
        # Texture drawn to screen
@@ -80,10 +122,10 @@ class Sprite
                center_direct = value
        end
 
-       # Rotation on the Z axis, positive values go counterclockwise
+       # Rotation on the Z axis, positive values turn counterclockwise
        var rotation = 0.0 is writable(rotation_direct=)
 
-       # Rotation on the Z axis, positive values go counterclockwise
+       # Rotation on the Z axis, positive values turn counterclockwise
        fun rotation=(value: Float)
        do
                if isset _rotation and value != rotation then needs_update
@@ -102,10 +144,12 @@ class Sprite
 
        # Scale applied to this sprite
        #
-       # The default size of `self` depends on the size in pixels of `texture`.
+       # The basic size of `self` depends on the size in pixels of `texture`.
        var scale = 1.0 is writable(scale_direct=)
 
-       # Scale applied to this sprite, see `scale`
+       # Scale applied to this sprite
+       #
+       # The basic size of `self` depends on the size in pixels of `texture`.
        fun scale=(value: Float)
        do
                if isset _scale and value != scale then needs_update
@@ -233,10 +277,10 @@ redef class App
        # Camera for `ui_sprites` using an orthogonal view
        var ui_camera = new UICamera(app.display.as(not null)) is lazy
 
-       # World sprites to draw as seen by `world_camera`
+       # World sprites drawn as seen by `world_camera`
        var sprites: Set[Sprite] = new SpriteSet
 
-       # UI sprites to draw as seen by `ui_camera`, drawn over world `sprites`
+       # UI sprites drawn as seen by `ui_camera`, over world `sprites`
        var ui_sprites: Set[Sprite] = new SpriteSet
 
        # Main method to refine in clients to update game logic and `sprites`
@@ -602,7 +646,7 @@ private class SpriteSet
        super HashSet[Sprite]
 
        # Map texture then static vs dynamic to a `SpriteContext`
-       var contexts_map = new HashMap2[GamnitRootTexture, Bool, SpriteContext]
+       var contexts_map = new HashMap2[RootTexture, Bool, SpriteContext]
 
        # Contexts in `contexts_map`
        var contexts_items = new Array[SpriteContext]
@@ -697,7 +741,7 @@ private class SpriteContext
        # Context config and state
 
        # Only root texture drawn by this context
-       var texture: nullable GamnitRootTexture
+       var texture: nullable RootTexture
 
        # OpenGL ES usage of `buffer_array` and `buffer_element`
        var usage: GLBufferUsage
@@ -1040,6 +1084,8 @@ end
 # The data can be compressed by a call to `defragment`.
 #
 # ~~~
+# intrude import gamnit::flat
+#
 # var array = new GroupedArray[String]
 # assert array.to_s == ""
 #
@@ -1199,6 +1245,8 @@ private class GroupedArray[E]
        # Returns the elements that moved as a list.
        #
        # ~~~
+       # intrude import gamnit::flat
+       #
        # var array = new GroupedArray[String]
        # array.add "a"
        # array.add "b"
index 9b980fb..6b47788 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Services to load textures, create subtextures and manage their life-cycle
+# Load textures, create subtextures and manage their life-cycle
 module textures
 
 import display
 
-# Abstract gamnit texture
+# Texture composed of pixels, loaded from the assets folder by default
+#
+# Most textures should be created with `App` (as attributes)
+# for the method `on_create` to load them.
+#
+# ~~~
+# import gamnit::flat
+#
+# redef class App
+#     # Create the texture object, it will be loaded automatically
+#     var texture = new Texture("path/in/assets.png")
+#
+#     redef fun on_create()
+#     do
+#         # Let `on_create` load the texture
+#         super
+#
+#         # Use the texture
+#         var sprite = new Sprite(texture, new Point3d[Float](0.0, 0.0, 0.0))
+#         app.sprites.add sprite
+#     end
+# end
+# ~~~
+#
+# Otherwise, they can be loaded and error checked explicitly after `on_create`.
+#
+# ~~~nitish
+# var texture = new Texture("path/in/assets.png")
+# texture.load
+# var error = texture.error
+# if error != null then print_error error
+# ~~~
+#
+# A texture may also be created programmatically, like `CheckerTexture`,
+# or derived from another texture, using `subtexture`.
+# Textures with actual pixel data (not `Subtexture`) are `RootTexture`.
+# Texture loaded from the assets folder may in the PNG or JPG formats.
 abstract class Texture
 
        # Prepare a texture located at `path` within the `assets` folder
        new (path: Text) do return new TextureAsset(path.to_s)
 
-       # Root texture of which `self` is derived
-       fun root: GamnitRootTexture is abstract
+       # Root texture from which `self` is derived
+       fun root: RootTexture is abstract
 
        # Width in pixels of this texture
        var width = 0.0
@@ -42,10 +78,10 @@ abstract class Texture
        fun gl_texture: Int do return root.gl_texture
 
        # Prepare a subtexture from this texture, from the given pixel offsets
-       fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
+       fun subtexture(left, top, width, height: Numeric): Subtexture
        do
                # Setup the subtexture
-               var subtex = new GamnitSubtexture(root, self, left.to_f, top.to_f, width.to_f, height.to_f)
+               var subtex = new Subtexture(root, self, left.to_f, top.to_f, width.to_f, height.to_f)
                return subtex
        end
 
@@ -64,7 +100,7 @@ end
 
 # Colorful small texture of 2x2 pixels
 class CheckerTexture
-       super GamnitRootTexture
+       super RootTexture
 
        redef fun load(force)
        do
@@ -84,7 +120,7 @@ class CheckerTexture
 end
 
 # Texture with its own pixels
-class GamnitRootTexture
+class RootTexture
        super Texture
 
        redef fun root do return self
@@ -143,7 +179,7 @@ end
 
 # Texture loaded from the assets folder
 class TextureAsset
-       super GamnitRootTexture
+       super RootTexture
 
        # Path to this texture within the `assets` folder
        var path: String
@@ -166,7 +202,7 @@ class TextureAsset
 end
 
 # Texture derived from another texture, does not own its pixels
-class GamnitSubtexture
+class Subtexture
        super Texture
 
        redef var root
index cf5d0f8..3dc158a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Provides interfaces and classes to represent basic geometry needs.
+# Interfaces and classes to represent basic geometry needs.
 module points_and_lines is serialize
 
 import serialization
 
-# An abstract 2d point, strongly linked to its implementation `Point`
+# Abstract 2d point, strongly linked to its implementation `Point`
 interface IPoint[N: Numeric]
 
        # Horizontal coordinate
@@ -84,7 +84,7 @@ interface IPoint[N: Numeric]
        redef fun ==(o) do return o isa IPoint[Numeric] and o.x == x and o.y == y
 end
 
-# A 2d point and an implementation of `IPoint`
+# 2D point with `x` and `z`
 class Point[N: Numeric]
        super IPoint[N]
 
@@ -92,17 +92,17 @@ class Point[N: Numeric]
        redef var y: N is writable
 end
 
-# An abstract 3d point, strongly linked to its implementation `Point3d`
+# Abstract 3d point, strongly linked to its implementation `Point3d`
 interface IPoint3d[N: Numeric]
        super IPoint[N]
 
-       # depth coordinate
+       # Depth coordinate
        fun z: N is abstract
 
        redef fun to_s do return "({x}, {y}, {z})"
 end
 
-# A 3d point and an implementation of `IPoint3d`
+# 3D point with `x`, `y` and `z`
 class Point3d[N: Numeric]
        super IPoint3d[N]
        super Point[N]
@@ -110,21 +110,21 @@ class Point3d[N: Numeric]
        redef var z: N is writable
 end
 
-# An abstract 2d line segment
+# Abstract 2D line segment between two ordered points
 interface ILine[N: Numeric]
        # The type of points that ends the segment
        type P: IPoint[N]
 
-       # The point that is the left-end of the segment
+       # Point at the left-end of the segment
        fun point_left: P is abstract
 
-       # The point that is the right-end of the segment
+       # Point at the right-end of the segment
        fun point_right: P is abstract
 
        redef fun to_s do return "{point_left}--{point_right}"
 end
 
-# A 2d line segment
+# 2D line segment between two ordered points
 class Line[N: Numeric]
        super ILine[N]
 
@@ -142,14 +142,14 @@ class Line[N: Numeric]
        end
 end
 
-# An abstract 3d line segment
+# Abstract 3D line segment between two ordered points
 interface ILine3d[N: Numeric]
        super ILine[N]
 
        redef type P: IPoint3d[N]
 end
 
-# A 3d line segment
+# 3D line segment between two ordered points
 class Line3d[N: Numeric]
        super Line[N]
        super ILine3d[N]