Skip to content
This repository has been archived by the owner on Oct 15, 2019. It is now read-only.

l3kn/linalg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LinAlg

Struct based linear algebra library

Vectors

Basic usage

This library includes some predefined vector structs:

  • LA::Vector1
  • LA::Vector2
  • LA::Vector3
  • LA::Vector4

These structs implement a basic set of functionality (let's take LA::Vector2 as an example):

require "linalg"
include LA # so we can use `Vector2` instead of `LA::Vector2`

# Predefined constants:
# * `COMPONENTS` (needed by the macros)
# Predefined class methods (to enable stuff like [v1, v2, v3].sum)
# * `zero`, `one`
# * one for each component

p Vector2::COMPONENTS # => [:x, :y]
p Vector2.zero        # => LA::Vector2(@x = 0.0, @y = 0.0)
p Vector2.one         # => LA::Vector2(@x = 1.0, @y = 1.0)
p Vector2.x           # => LA::Vector2(@x = 1.0, @y = 0.0)
p Vector2.y           # => LA::Vector2(@x = 0.0, @y = 1.0)

a = Vector2.new(1.0, 2.0)
b = Vector2.new(3.0, 4.0)

p -b                  # => LA::Vector2(@x = -3.0, @y = -4.0)

p a + b               # => LA::Vector2(@x = 4.0, @y = 6.0)
p a - b               # => LA::Vector2(@x = -2.0, @y = -2.0)

p b.length            # => 5.0
p b.squared_length    # => 25.0

p b * 2.0             # => LA::Vector2(@x = 6.0, @y = 8.0)
p b / 2.0             # => LA::Vector2(@x = 1.5, @y = 2.0)

p a.dot(b)            # => 11.0

# The cross product is only implemented for Vector3
x = Vector3.x
y = Vector3.y

p x.cross(y)          # => LA::Vector3(@x = 0.0, @y = 0.0, @z = 1.0)

Custom Structs

Because it is (currently?) not possible for structs to inherit from non-abstract structs (like LA::Vector3), there are abstract versions of all the VectorN structs:

  • LA::AVector1
  • LA::AVector2
  • LA::AVector3
  • LA::AVector4

These implement all of the VectorN functions, in fact the “implementation” von LA::Vector3 looks like this:

struct Vector3 < AVector3
end

Example 1

require "linalg"

struct Normal < LA::AVector3
  # The methods of `LA::AVectors3` are defined
  # using the following macros
  # `define_vector_op(:+)`
  # `define_vector_op(:-)`
  # `define_unop(:-)`
  # `define_op(:*)`
  # `define_op(:/)`
  # `define_dot`
  # `define_length` (defines `.length`, `.squared_length` and `.normalize`)

  # In this example, we need to overwrite some of those,
  # because the result of (e.g.) adding two normals
  # is not neccessarily a “valid” normal.
  # 
  # Luckily there are extended forms of the macros from before
  # that we can use overwrite the old methods
  # and specify a new type for the result

  define_vector_op(:+, other_class: Normal, result_class: LA::Vector3)
  define_vector_op(:-, other_class: Normal, result_class: LA::Vector3)

  define_op(:*, result_class: LA::Vector3)
  define_op(:/, result_class: LA::Vector3)

  # Additionally we could define those ops with types other than `Float64`

  define_op(:*, other_class: Int32, result_class: LA::Vector3)
  define_op(:/, other_class: Int32, result_class: LA::Vector3)

  def self.one
    raise "This makes no sense in the context of a normal"
  end

  def self.zero
    raise "This makes no sense in the context of a normal"
  end
end

Example 2

Let's make an even more “custom” struct!

require "linalg"

# In this case we can not inherit from `LA::Vector3`,
# because our components have names other than `.x`, `.y`, `.z`.

struct Color
  COMPONENTS = [:r, :g, :b]

  # `define_vector` defines all the vector methods
  # (see: __Example 1__) at once
  define_vector

  # Multiplication of two vectors is not defined by default,
  # but here it is handy to blend two colors
  define_vector_op(:*)
end

red = Color::R
other = Color.new(0.8, 0.2, 0.7)

p red * other # => Color(@r=0.8, @g=0.0, @b=0.0)

Swizzling

Swizzling is not „enabled“ (the macros are not included) for the default vector structs, but it can be included via the define_swizzling(target_size, target = :tuple, signed = false) macro.

struct Vec2 < LA::AVector2
  # By default, the swizzled methods return a tuple with the values
  define_vector_swizzling(2)
end

struct Vec3 < LA::AVector3
  # The other valid options are to pass `:array` or a class name as the `target`
  define_vector_swizzling(3, target: :array)

  # If `signed` is set to `true`,
  # in addition to methods like `vector.xyz`
  # the macro will create “signed” methods
  # like `vector._x_y_z` or `.vector.x_zy`
  define_vector_swizzling(2, target: Vec2, signed: true)
end

a = Vec2.new(1.0, 2.0)
p a.xy # => {1.0, 2.0}
p a.yx # => {2.0, 1.0}
p a.yy # => {2.0, 2.0}

b = Vec3.new(1.0, 2.0, 3.0)
p b.xxz # => [1.0, 1.0, 3.0]
p b.xy  # => Vec2(@x = 1.0, @y = 2.0)
p b._y_y  # => Vec2(@x = -2.0, @y = -2.0)

Releases

No releases published

Packages

No packages published