Update to Elixir 1.6

* Run code formatter.
* Credo doesn't like `mix format`'s line length.
* Fix failing tests re `Angle`.
This commit is contained in:
James Harton 2018-01-19 10:40:56 +13:00
parent 1259f263ed
commit f74836dfd3
38 changed files with 537 additions and 332 deletions

146
.credo.exs Normal file
View file

@ -0,0 +1,146 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
# For some checks, you can also set other parameters
#
# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false},
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
{Credo.Check.Warning.NameRedeclarationByCase, false},
{Credo.Check.Warning.NameRedeclarationByDef, false},
{Credo.Check.Warning.NameRedeclarationByFn, false},
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}

View file

@ -2,11 +2,13 @@ defimpl Inspect, for: Kinemat.Cartesian do
alias Kinemat.Cartesian alias Kinemat.Cartesian
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Cartesian.t, any) :: String.t @spec inspect(Cartesian.t(), any) :: String.t()
def inspect(angle, opts) do def inspect(angle, opts) do
angle = angle angle =
|> Map.from_struct angle
|> Map.from_struct()
|> Enum.into([]) |> Enum.into([])
concat ["#Kinemat.Point<", to_doc(angle, opts), ">"]
concat(["#Kinemat.Point<", to_doc(angle, opts), ">"])
end end
end end

View file

@ -2,11 +2,13 @@ defimpl Inspect, for: Kinemat.Cylindrical do
alias Kinemat.Cylindrical alias Kinemat.Cylindrical
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Cylindrical.t, any) :: String.t @spec inspect(Cylindrical.t(), any) :: String.t()
def inspect(angle, opts) do def inspect(angle, opts) do
angle = angle angle =
|> Map.from_struct angle
|> Map.from_struct()
|> Enum.into([]) |> Enum.into([])
concat ["#Kinemat.Point<", to_doc(angle, opts), ">"]
concat(["#Kinemat.Point<", to_doc(angle, opts), ">"])
end end
end end

View file

@ -2,9 +2,9 @@ defimpl Inspect, for: Kinemat.Euler do
alias Kinemat.{Euler, Orientation} alias Kinemat.{Euler, Orientation}
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Orientation.t, any) :: String.t @spec inspect(Orientation.t(), any) :: String.t()
def inspect(%Euler{representation: r, x: x, y: y, z: z}, opts) do def inspect(%Euler{representation: r, x: x, y: y, z: z}, opts) do
values = [euler: r, x: x, y: y, z: z] values = [euler: r, x: x, y: y, z: z]
concat ["#Kinemat.Orientation<", to_doc(values, opts), ">"] concat(["#Kinemat.Orientation<", to_doc(values, opts), ">"])
end end
end end

View file

@ -2,11 +2,13 @@ defimpl Inspect, for: Kinemat.Frame do
alias Kinemat.Frame alias Kinemat.Frame
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Frame.t, any) :: String.t @spec inspect(Frame.t(), any) :: String.t()
def inspect(plane, opts) do def inspect(plane, opts) do
plane = plane plane =
|> Map.from_struct plane
|> Map.from_struct()
|> Enum.into([]) |> Enum.into([])
concat ["#Kinemat.Frame<", to_doc(plane, opts), ">"]
concat(["#Kinemat.Frame<", to_doc(plane, opts), ">"])
end end
end end

View file

@ -2,9 +2,9 @@ defimpl Inspect, for: Kinemat.Quaternion do
alias Kinemat.{Quaternion, Orientation} alias Kinemat.{Quaternion, Orientation}
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Orientation.t, any) :: String.t @spec inspect(Orientation.t(), any) :: String.t()
def inspect(%Quaternion{w: w, x: x, y: y, z: z}, opts) do def inspect(%Quaternion{w: w, x: x, y: y, z: z}, opts) do
values = [type: :quaternion, w: w, x: x, y: y, z: z] values = [type: :quaternion, w: w, x: x, y: y, z: z]
concat ["#Kinemat.Orientation<", to_doc(values, opts), ">"] concat(["#Kinemat.Orientation<", to_doc(values, opts), ">"])
end end
end end

View file

@ -2,13 +2,20 @@ defimpl Inspect, for: Kinemat.RotationMatrix do
alias Kinemat.{RotationMatrix, Orientation} alias Kinemat.{RotationMatrix, Orientation}
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Orientation.t, any) :: String.t @spec inspect(Orientation.t(), any) :: String.t()
def inspect(%RotationMatrix{matrix: {m00, m01, m02, m10, m11, m12, m20, m21, m22}}, opts) do def inspect(
concat ["#Kinemat.Orientation<", to_doc([ %RotationMatrix{matrix: {m00, m01, m02, m10, m11, m12, m20, m21, m22}},
rotation_matrix: { opts
{m00, m01, m02}, ) do
{m10, m11, m12}, concat([
{m20, m21, m22}} "#Kinemat.Orientation<",
], opts), ">"] to_doc(
[
rotation_matrix: {{m00, m01, m02}, {m10, m11, m12}, {m20, m21, m22}}
],
opts
),
">"
])
end end
end end

View file

@ -1,11 +1,13 @@
defimpl Inspect, for: Kinemat.Spherical do defimpl Inspect, for: Kinemat.Spherical do
import Inspect.Algebra import Inspect.Algebra
@spec inspect(Angle.t, any) :: String.t @spec inspect(Angle.t(), any) :: String.t()
def inspect(angle, opts) do def inspect(angle, opts) do
angle = angle angle =
|> Map.from_struct angle
|> Map.from_struct()
|> Enum.into([]) |> Enum.into([])
concat ["#Kinemat.Point<", to_doc(angle, opts), ">"]
concat(["#Kinemat.Point<", to_doc(angle, opts), ">"])
end end
end end

View file

@ -1,10 +1,19 @@
defmodule Kinemat do defmodule Kinemat do
defmacro __using__(_opts) do defmacro __using__(_opts) do
quote do quote do
alias Kinemat.{Point, Cartesian, Cylindrical, Spherical, alias Kinemat.{
Orientation, Euler, RotationMatrix, Quaternion, Point,
Cartesian,
Cylindrical,
Spherical,
Orientation,
Euler,
RotationMatrix,
Quaternion,
# Joint, Point, Prismatic, Radians, Revolute, Spherical, # Joint, Point, Prismatic, Radians, Revolute, Spherical,
Frame} Frame
}
import Angle.Sigil import Angle.Sigil
end end
end end
@ -21,5 +30,4 @@ defmodule Kinemat do
...> Euler.init(:xyz, ~a(0), ~a(0), ~a(0)) ...> Euler.init(:xyz, ~a(0), ~a(0), ~a(0))
#Kinemat.Orientation<[euler: :xyz, x: #Angle<0>, y: #Angle<0>, z: #Angle<0>]> #Kinemat.Orientation<[euler: :xyz, x: #Angle<0>, y: #Angle<0>, z: #Angle<0>]>
""" """
end end

View file

@ -6,9 +6,7 @@ defmodule Kinemat.Cartesian do
Describes a point in three-dimensional space using cartesian coordinates (x, y, z). Describes a point in three-dimensional space using cartesian coordinates (x, y, z).
""" """
@type t :: %Cartesian{x: number, @type t :: %Cartesian{x: number, y: number, z: number}
y: number,
z: number}
@doc """ @doc """
Initialise a cartesian point from `x`, `y` and `z`. Initialise a cartesian point from `x`, `y` and `z`.

View file

@ -6,9 +6,7 @@ defmodule Kinemat.Cylindrical do
Describes a point in cylindrical coordinates. Describes a point in cylindrical coordinates.
""" """
@type t :: %Cylindrical{radial: number, @type t :: %Cylindrical{radial: number, azimuth: Angle.t(), vertical: number}
azimuth: Angle.t,
vertical: number}
@doc """ @doc """
Initalise a cylindrical coordinate from `rho`, `theta`, `z` (ρ,θ,z). Initalise a cylindrical coordinate from `rho`, `theta`, `z` (ρ,θ,z).
@ -25,7 +23,7 @@ defmodule Kinemat.Cylindrical do
azimuth: ~a(0.5)r, azimuth: ~a(0.5)r,
vertical: 30} vertical: 30}
""" """
@spec init(number, Angle.t, number) :: t @spec init(number, Angle.t(), number) :: t
def init(rho, %Angle{} = theta, z) do def init(rho, %Angle{} = theta, z) do
%Cylindrical{radial: rho, azimuth: theta, vertical: z} %Cylindrical{radial: rho, azimuth: theta, vertical: z}
end end
@ -51,7 +49,7 @@ defmodule Kinemat.Cylindrical do
@doc """ @doc """
Alias for `azimuth/1`. Alias for `azimuth/1`.
""" """
@spec theta(t) :: Angle.t @spec theta(t) :: Angle.t()
def theta(point), do: point |> azimuth() def theta(point), do: point |> azimuth()
@doc """ @doc """
@ -63,7 +61,7 @@ defmodule Kinemat.Cylindrical do
...> |> Cylindrical.azimuth() ...> |> Cylindrical.azimuth()
#Angle<0.5㎭> #Angle<0.5㎭>
""" """
@spec azimuth(t) :: Angle.t @spec azimuth(t) :: Angle.t()
def azimuth(%Cylindrical{azimuth: theta}), do: theta def azimuth(%Cylindrical{azimuth: theta}), do: theta
@doc """ @doc """

View file

@ -9,8 +9,12 @@ defmodule Kinemat.Euler do
YZX, ZXY. YZX, ZXY.
""" """
@type valid_representation :: :xyz | :yxz | :zxy | :zyx | :yzx | :xzy @type valid_representation :: :xyz | :yxz | :zxy | :zyx | :yzx | :xzy
@type t :: %Euler{representation: valid_representation, @type t :: %Euler{
x: Angle.t, y: Angle.t, z: Angle.t} representation: valid_representation,
x: Angle.t(),
y: Angle.t(),
z: Angle.t()
}
@doc """ @doc """
Initialise a new Euler orientation with angles. Initialise a new Euler orientation with angles.
@ -28,10 +32,9 @@ defmodule Kinemat.Euler do
y: ~a(20)d, y: ~a(20)d,
z: ~a(30)d} z: ~a(30)d}
""" """
@spec init(valid_representation, Angle.t, Angle.t, Angle.t) :: t @spec init(valid_representation, Angle.t(), Angle.t(), Angle.t()) :: t
def init(representation, x, y, z) def init(representation, %Angle{} = x, %Angle{} = y, %Angle{} = z)
when representation in ~w(xyz yxz zxy zyx yzx xzy)a when representation in ~w(xyz yxz zxy zyx yzx xzy)a do
do
%Euler{representation: representation, x: x, y: y, z: z} %Euler{representation: representation, x: x, y: y, z: z}
end end
@ -62,7 +65,7 @@ defmodule Kinemat.Euler do
...> |> Euler.x ...> |> Euler.x
#Angle<13°> #Angle<13°>
""" """
@spec x(t) :: Angle.t @spec x(t) :: Angle.t()
def x(%Euler{x: x}), do: x def x(%Euler{x: x}), do: x
@doc """ @doc """
@ -74,7 +77,7 @@ defmodule Kinemat.Euler do
...> |> Euler.y ...> |> Euler.y
#Angle<13°> #Angle<13°>
""" """
@spec y(t) :: Angle.t @spec y(t) :: Angle.t()
def y(%Euler{y: y}), do: y def y(%Euler{y: y}), do: y
@doc """ @doc """
@ -86,6 +89,6 @@ defmodule Kinemat.Euler do
...> |> Euler.z ...> |> Euler.z
#Angle<13°> #Angle<13°>
""" """
@spec z(t) :: Angle.t @spec z(t) :: Angle.t()
def z(%Euler{z: z}), do: z def z(%Euler{z: z}), do: z
end end

View file

@ -12,7 +12,7 @@ defmodule Kinemat.Euler.ToQuaternion do
@doc """ @doc """
Converts Euler orientations into Quaternions. Converts Euler orientations into Quaternions.
""" """
@spec to_quaternion(Euler.t) :: Quaternion.t @spec to_quaternion(Euler.t()) :: Quaternion.t()
def to_quaternion(%Euler{representation: order, x: x, y: y, z: z}) do def to_quaternion(%Euler{representation: order, x: x, y: y, z: z}) do
{_, x} = Angle.to_radians(x) {_, x} = Angle.to_radians(x)
{_, y} = Angle.to_radians(y) {_, y} = Angle.to_radians(y)

View file

@ -11,7 +11,7 @@ defmodule Kinemat.Euler.ToRotationMatrix do
@doc """ @doc """
Convert an Euler orientation into a rotation matrix. Convert an Euler orientation into a rotation matrix.
""" """
@spec to_rotation_matrix(Euler.t) :: RotationMatrix.t @spec to_rotation_matrix(Euler.t()) :: RotationMatrix.t()
def to_rotation_matrix(%Euler{representation: order, x: x, y: y, z: z}) do def to_rotation_matrix(%Euler{representation: order, x: x, y: y, z: z}) do
a = cos(x) a = cos(x)
b = sin(x) b = sin(x)
@ -22,7 +22,7 @@ defmodule Kinemat.Euler.ToRotationMatrix do
order order
|> build_rotation({a, b, c, d, e, f}) |> build_rotation({a, b, c, d, e, f})
|> RotationMatrix.init |> RotationMatrix.init()
end end
defp build_rotation(:xyz, {a, b, c, d, e, f}) do defp build_rotation(:xyz, {a, b, c, d, e, f}) do
@ -32,20 +32,18 @@ defmodule Kinemat.Euler.ToRotationMatrix do
bf = b * f bf = b * f
m00 = c * e m00 = c * e
m10 = - c * f m10 = -c * f
m20 = d m20 = d
m01 = af + be * d m01 = af + be * d
m11 = ae - bf * d m11 = ae - bf * d
m21 = - b * c m21 = -b * c
m02 = bf - ae * d m02 = bf - ae * d
m12 = be + af * d m12 = be + af * d
m22 = a * c m22 = a * c
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
defp build_rotation(:yxz, {a, b, c, d, e, f}) do defp build_rotation(:yxz, {a, b, c, d, e, f}) do
@ -60,15 +58,13 @@ defmodule Kinemat.Euler.ToRotationMatrix do
m01 = a * f m01 = a * f
m11 = a * e m11 = a * e
m21 = - b m21 = -b
m02 = cf * b - de m02 = cf * b - de
m12 = df + ce * b m12 = df + ce * b
m22 = a * c m22 = a * c
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
defp build_rotation(:zxy, {a, b, c, d, e, f}) do defp build_rotation(:zxy, {a, b, c, d, e, f}) do
@ -78,20 +74,18 @@ defmodule Kinemat.Euler.ToRotationMatrix do
df = d * f df = d * f
m00 = ce - df * b m00 = ce - df * b
m10 = - a * f m10 = -a * f
m20 = de + cf * b m20 = de + cf * b
m01 = cf + de * b m01 = cf + de * b
m11 = a * e m11 = a * e
m21 = df - ce * b m21 = df - ce * b
m02 = - a * d m02 = -a * d
m12 = b m12 = b
m22 = a * c m22 = a * c
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
defp build_rotation(:zyx, {a, b, c, d, e, f}) do defp build_rotation(:zyx, {a, b, c, d, e, f}) do
@ -108,13 +102,11 @@ defmodule Kinemat.Euler.ToRotationMatrix do
m11 = bf * d + ae m11 = bf * d + ae
m21 = af * d - be m21 = af * d - be
m02 = - d m02 = -d
m12 = b * c m12 = b * c
m22 = a * c m22 = a * c
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
defp build_rotation(:yzx, {a, b, c, d, e, f}) do defp build_rotation(:yzx, {a, b, c, d, e, f}) do
@ -129,15 +121,13 @@ defmodule Kinemat.Euler.ToRotationMatrix do
m01 = f m01 = f
m11 = a * e m11 = a * e
m21 = - b * e m21 = -b * e
m02 = - d * e m02 = -d * e
m12 = ad * f + bc m12 = ad * f + bc
m22 = ac - bd * f m22 = ac - bd * f
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
defp build_rotation(:xzy, {a, b, c, d, e, f}) do defp build_rotation(:xzy, {a, b, c, d, e, f}) do
@ -147,7 +137,7 @@ defmodule Kinemat.Euler.ToRotationMatrix do
bd = b * d bd = b * d
m00 = c * e m00 = c * e
m10 = - f m10 = -f
m20 = d * e m20 = d * e
m01 = ac * f + bd m01 = ac * f + bd
@ -158,9 +148,6 @@ defmodule Kinemat.Euler.ToRotationMatrix do
m12 = b * e m12 = b * e
m22 = bd * f + ac m22 = bd * f + ac
{m00, m01, m02, {m00, m01, m02, m10, m11, m12, m20, m21, m22}
m10, m11, m12,
m20, m21, m22}
end end
end end

View file

@ -7,17 +7,15 @@ defmodule Kinemat.Frame do
parent reference frame. parent reference frame.
""" """
@type t :: %Frame{point: Point.t, @type t :: %Frame{point: Point.t(), orientation: Orientation.t()}
orientation: Orientation.t}
@doc """ @doc """
Initialise a frame from a point and an orientation. Initialise a frame from a point and an orientation.
""" """
@spec init(Point.t, Orientation.t) :: t @spec init(Point.t(), Orientation.t()) :: t
def init(%{__struct__: p} = point, %{__struct__: o} = orientation) def init(%{__struct__: p} = point, %{__struct__: o} = orientation)
when (p == Cartesian or p == Cylindrical or p == Spherical) when (p == Cartesian or p == Cylindrical or p == Spherical) and
and (o == Euler or o == Quaternion or o == RotationMatrix) (o == Euler or o == Quaternion or o == RotationMatrix) do
do
%Frame{point: point, orientation: orientation} %Frame{point: point, orientation: orientation}
end end
@ -30,7 +28,7 @@ defmodule Kinemat.Frame do
...> |> Frame.point() ...> |> Frame.point()
#Kinemat.Point<[x: 3, y: 4, z: 5]> #Kinemat.Point<[x: 3, y: 4, z: 5]>
""" """
@spec point(t) :: Point.t @spec point(t) :: Point.t()
def point(%Frame{point: p}), do: p def point(%Frame{point: p}), do: p
@doc """ @doc """
@ -42,6 +40,6 @@ defmodule Kinemat.Frame do
...> |> Frame.orientation() ...> |> Frame.orientation()
#Kinemat.Orientation<[euler: :xyz, x: #Angle<0>, y: #Angle<0>, z: #Angle<0>]> #Kinemat.Orientation<[euler: :xyz, x: #Angle<0>, y: #Angle<0>, z: #Angle<0>]>
""" """
@spec orientation(t) :: Orientation.t @spec orientation(t) :: Orientation.t()
def orientation(%Frame{orientation: o}), do: o def orientation(%Frame{orientation: o}), do: o
end end

View file

@ -5,10 +5,9 @@ defmodule Kinemat.HomogeneousTransformation do
Converts a Frame into a 4x4 matrix of it's homogeneous transformation. Converts a Frame into a 4x4 matrix of it's homogeneous transformation.
""" """
@type t :: {number, number, number, number, @type t ::
number, number, number, number, {number, number, number, number, number, number, number, number,
number, number, number, number, number, number, number, number, number, number, number, number}
number, number, number, number}
@doc """ @doc """
Converts a Frame into it's 4x4 matrix representation as a homogeneous transformation. Converts a Frame into it's 4x4 matrix representation as a homogeneous transformation.
@ -28,28 +27,31 @@ defmodule Kinemat.HomogeneousTransformation do
0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0} 0.0, 0.0, 0.0, 1.0}
""" """
@spec to_homogeneous_transformation(Frame.t) :: t @spec to_homogeneous_transformation(Frame.t()) :: t
def to_homogeneous_transformation(%Frame{point: point, orientation: orientation}) do def to_homogeneous_transformation(%Frame{
{m00, m01, m02, point: point,
m10, m11, m12, orientation: orientation
m20, m21, m22} = matrix_from(orientation) }) do
{m00, m01, m02, m10, m11, m12, m20, m21, m22} = matrix_from(orientation)
{m03, m13, m23} = coords_from(point) {m03, m13, m23} = coords_from(point)
{m00, m01, m02, m03, {m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, 0.0, 0.0, 0.0,
m10, m11, m12, m13, 1.0}
m20, m21, m22, m23,
0.0, 0.0, 0.0, 1.0}
end end
defp coords_from(point) do defp coords_from(point) do
%{x: x, y: y, z: z} = point %{x: x, y: y, z: z} =
point
|> Point.to_cartesian() |> Point.to_cartesian()
{x, y, z} {x, y, z}
end end
defp matrix_from(orientation) do defp matrix_from(orientation) do
%{matrix: matrix} = orientation %{matrix: matrix} =
orientation
|> Orientation.to_rotation_matrix() |> Orientation.to_rotation_matrix()
matrix matrix
end end
end end

View file

@ -1,5 +0,0 @@
defprotocol Kinemat.Joint do
@moduledoc """
FIXME Not yet done.
"""
end

View file

@ -6,18 +6,18 @@ defprotocol Kinemat.Orientation do
different orientations (Euler, RotationMatrix and Quaternion). different orientations (Euler, RotationMatrix and Quaternion).
""" """
@type t :: Euler.t | RotationMatrix.t | Quaternion.t @type t :: Euler.t() | RotationMatrix.t() | Quaternion.t()
@doc """ @doc """
Convert the Orientation into it's Rotation Matrix representation. Convert the Orientation into it's Rotation Matrix representation.
""" """
@spec to_rotation_matrix(Orientation.t) :: RotationMatrix.t @spec to_rotation_matrix(Orientation.t()) :: RotationMatrix.t()
def to_rotation_matrix(orientation) def to_rotation_matrix(orientation)
@doc """ @doc """
Convert the Orientation into it's Quaternion representation. Convert the Orientation into it's Quaternion representation.
""" """
@spec to_quaternion(Orientation.t) :: Quaternion.t @spec to_quaternion(Orientation.t()) :: Quaternion.t()
def to_quaternion(orientation) def to_quaternion(orientation)
@doc """ @doc """
@ -25,13 +25,13 @@ defprotocol Kinemat.Orientation do
Default representation is `:xyz`. Default representation is `:xyz`.
""" """
@spec to_euler(Orientation.t) :: Euler.t @spec to_euler(Orientation.t()) :: Euler.t()
def to_euler(orientation) def to_euler(orientation)
@doc """ @doc """
Convert the Orientation into it's Euler angle representation in the Convert the Orientation into it's Euler angle representation in the
specified axis order. specified axis order.
""" """
@spec to_euler(Orientation.t, Euler.valid_representation) :: Euler.t @spec to_euler(Orientation.t(), Euler.valid_representation()) :: Euler.t()
def to_euler(orientation, representation) def to_euler(orientation, representation)
end end

View file

@ -17,8 +17,9 @@ defimpl Kinemat.Orientation, for: Kinemat.Euler do
-0.33036608954935215, 0.7695370178986853, 0.5465080282662533, -0.33036608954935215, 0.7695370178986853, 0.5465080282662533,
0.25881904510252074, -0.4829629131445341, 0.8365163037378079}} 0.25881904510252074, -0.4829629131445341, 0.8365163037378079}}
""" """
@spec to_rotation_matrix(Euler.t) :: RotationMatrix.t @spec to_rotation_matrix(Euler.t()) :: RotationMatrix.t()
def to_rotation_matrix(euler), do: Euler.ToRotationMatrix.to_rotation_matrix(euler) def to_rotation_matrix(euler),
do: Euler.ToRotationMatrix.to_rotation_matrix(euler)
@doc """ @doc """
Convert the euler angle into quaternion. Convert the euler angle into quaternion.
@ -32,8 +33,9 @@ defimpl Kinemat.Orientation, for: Kinemat.Euler do
%Kinemat.Quaternion{w: ~a(0.9372468582005039)r, x: 0.27459973122432013, %Kinemat.Quaternion{w: ~a(0.9372468582005039)r, x: 0.27459973122432013,
y: 0.07960424450132775, z: 0.19956572516889892} y: 0.07960424450132775, z: 0.19956572516889892}
""" """
@spec to_quaternion(Euler.t) :: Quaternion.t @spec to_quaternion(Euler.t()) :: Quaternion.t()
def to_quaternion(orientation), do: Euler.ToQuaternion.to_quaternion(orientation) def to_quaternion(orientation),
do: Euler.ToQuaternion.to_quaternion(orientation)
@doc """ @doc """
Convert the Euler from one representation to another. Convert the Euler from one representation to another.
@ -49,11 +51,14 @@ defimpl Kinemat.Orientation, for: Kinemat.Euler do
y: %Angle{r: -0.2617993877991494}, y: %Angle{r: -0.2617993877991494},
z: %Angle{r: -0.3490658503988659}} z: %Angle{r: -0.3490658503988659}}
""" """
@spec to_euler(Euler.t) :: Euler.t @spec to_euler(Euler.t()) :: Euler.t()
def to_euler(%Euler{} = orientation), do: orientation def to_euler(%Euler{} = orientation), do: orientation
@spec to_euler(Euler.t, Euler.valid_representation) :: Euler.t @spec to_euler(Euler.t(), Euler.valid_representation()) :: Euler.t()
def to_euler(%Euler{representation: r} = orientation, representation) when r == representation, do: orientation def to_euler(%Euler{representation: r} = orientation, representation)
when r == representation,
do: orientation
def to_euler(orientation, representation) do def to_euler(orientation, representation) do
orientation orientation
|> Orientation.to_rotation_matrix() |> Orientation.to_rotation_matrix()

View file

@ -23,9 +23,10 @@ defimpl Kinemat.Orientation, for: Kinemat.Quaternion do
}} }}
""" """
@spec to_rotation_matrix(Quaternion.t) :: RotationMatrix.t @spec to_rotation_matrix(Quaternion.t()) :: RotationMatrix.t()
def to_rotation_matrix(%Quaternion{w: w, x: x, y: y, z: z}) do def to_rotation_matrix(%Quaternion{w: w, x: x, y: y, z: z}) do
{_, r} = Angle.to_radians(w) {_, r} = Angle.to_radians(w)
r r
|> Quatern.create(x, y, z) |> Quatern.create(x, y, z)
|> Quatern.to_rotation_matrix() |> Quatern.to_rotation_matrix()
@ -35,7 +36,7 @@ defimpl Kinemat.Orientation, for: Kinemat.Quaternion do
@doc """ @doc """
Does nothing, simply returns the Quaternion. Does nothing, simply returns the Quaternion.
""" """
@spec to_quaternion(Quaternion.t) :: Quaternion.t @spec to_quaternion(Quaternion.t()) :: Quaternion.t()
def to_quaternion(orientation), do: orientation def to_quaternion(orientation), do: orientation
@doc """ @doc """
@ -51,7 +52,7 @@ defimpl Kinemat.Orientation, for: Kinemat.Quaternion do
y: ~a(0.2945952073784124)r, y: ~a(0.2945952073784124)r,
z: ~a(0.12377521877974283)r} z: ~a(0.12377521877974283)r}
""" """
@spec to_euler(Quaternion.t, Euler.valid_representation) :: Euler.t @spec to_euler(Quaternion.t(), Euler.valid_representation()) :: Euler.t()
def to_euler(orientation, representation \\ :xyz) do def to_euler(orientation, representation \\ :xyz) do
orientation orientation
|> Orientation.to_rotation_matrix() |> Orientation.to_rotation_matrix()

View file

@ -10,7 +10,7 @@ defimpl Kinemat.Orientation, for: Kinemat.RotationMatrix do
@doc """ @doc """
Does nothing, simply returns the rotation matrix. Does nothing, simply returns the rotation matrix.
""" """
@spec to_rotation_matrix(RotationMatrix.t) :: RotationMatrix.t @spec to_rotation_matrix(RotationMatrix.t()) :: RotationMatrix.t()
def to_rotation_matrix(orientation), do: orientation def to_rotation_matrix(orientation), do: orientation
@doc """ @doc """
@ -24,10 +24,12 @@ defimpl Kinemat.Orientation, for: Kinemat.RotationMatrix do
...> |> Orientation.to_quaternion() ...> |> Orientation.to_quaternion()
%Kinemat.Quaternion{w: ~a(0.7071067811865476)r, x: 0.7071067811865475, y: 0.0, z: 0.0} %Kinemat.Quaternion{w: ~a(0.7071067811865476)r, x: 0.7071067811865475, y: 0.0, z: 0.0}
""" """
@spec to_quaternion(RotationMatrix.t) :: Quaternion.t @spec to_quaternion(RotationMatrix.t()) :: Quaternion.t()
def to_quaternion(%RotationMatrix{matrix: matrix}) do def to_quaternion(%RotationMatrix{matrix: matrix}) do
{w, x, y, z} = matrix {w, x, y, z} =
matrix
|> Quatern.from_rotation_matrix() |> Quatern.from_rotation_matrix()
Quaternion.init(Radian.init(w), x, y, z) Quaternion.init(Radian.init(w), x, y, z)
end end
@ -41,13 +43,15 @@ defimpl Kinemat.Orientation, for: Kinemat.RotationMatrix do
...> |> RotationMatrix.init() ...> |> RotationMatrix.init()
...> |> Orientation.to_euler() ...> |> Orientation.to_euler()
%Kinemat.Euler{representation: :xyz, %Kinemat.Euler{representation: :xyz,
x: %Angle{r: 3.141592653589793}, x: ~a(3.141592653589793)r,
y: %Angle{r: 0.0}, y: ~a(0),
z: %Angle{r: -1.5707963267948966}} z: ~a(-1.5707963267948966)r}
""" """
@spec to_euler(RotationMatrix.t) :: Euler.t @spec to_euler(RotationMatrix.t()) :: Euler.t()
def to_euler(orientation), do: RotationMatrix.ToEuler.to_euler(orientation, :xyz) def to_euler(orientation),
do: RotationMatrix.ToEuler.to_euler(orientation, :xyz)
@spec to_euler(RotationMatrix.t, Euler.valid_orientation) :: Euler.t @spec to_euler(RotationMatrix.t(), Euler.valid_orientation()) :: Euler.t()
def to_euler(orientation, representation), do: RotationMatrix.ToEuler.to_euler(orientation, representation) def to_euler(orientation, representation),
do: RotationMatrix.ToEuler.to_euler(orientation, representation)
end end

View file

@ -5,23 +5,23 @@ defprotocol Kinemat.Point do
Describes a Point in 3 dimensional space. Describes a Point in 3 dimensional space.
""" """
@type t :: Cartesian.t | Cylindrical.t | Spherical.t @type t :: Cartesian.t() | Cylindrical.t() | Spherical.t()
@doc """ @doc """
Convert the Point into it's cartesian representation. Convert the Point into it's cartesian representation.
""" """
@spec to_cartesian(Point.t) :: Cartesian.t @spec to_cartesian(Point.t()) :: Cartesian.t()
def to_cartesian(point) def to_cartesian(point)
@doc """ @doc """
Convert the Point into it's cylindrical representation. Convert the Point into it's cylindrical representation.
""" """
@spec to_cylindrical(Point.t) :: Cylindrical.t @spec to_cylindrical(Point.t()) :: Cylindrical.t()
def to_cylindrical(point) def to_cylindrical(point)
@doc """ @doc """
Convert the Point into it's spherical representation. Convert the Point into it's spherical representation.
""" """
@spec to_spherical(Point.t) :: Spherical.t @spec to_spherical(Point.t()) :: Spherical.t()
def to_spherical(point) def to_spherical(point)
end end

View file

@ -12,7 +12,7 @@ defimpl Kinemat.Point, for: Kinemat.Cartesian do
...> |> Kinemat.Point.to_cartesian() ...> |> Kinemat.Point.to_cartesian()
%Kinemat.Cartesian{x: 1, y: 2, z: 3} %Kinemat.Cartesian{x: 1, y: 2, z: 3}
""" """
@spec to_cartesian(Cartesian.t) :: Cartesian.t @spec to_cartesian(Cartesian.t()) :: Cartesian.t()
def to_cartesian(%Cartesian{} = point), do: point def to_cartesian(%Cartesian{} = point), do: point
@doc """ @doc """
@ -27,7 +27,7 @@ defimpl Kinemat.Point, for: Kinemat.Cartesian do
azimuth: %Angle{r: 0.9272952180016122}, azimuth: %Angle{r: 0.9272952180016122},
vertical: 7} vertical: 7}
""" """
@spec to_cylindrical(Cartesian.t) :: Cylindrical.t @spec to_cylindrical(Cartesian.t()) :: Cylindrical.t()
def to_cylindrical(%Cartesian{x: x, y: y, z: z}) do def to_cylindrical(%Cartesian{x: x, y: y, z: z}) do
r = sqrt(pow(x, 2) + pow(y, 2)) r = sqrt(pow(x, 2) + pow(y, 2))
theta = Radian.init(atan(y / x)) theta = Radian.init(atan(y / x))
@ -46,7 +46,7 @@ defimpl Kinemat.Point, for: Kinemat.Cartesian do
azimuth: %Angle{r: 0.4234308319224211}, azimuth: %Angle{r: 0.4234308319224211},
polar: %Angle{r: 0.982793723247329}} polar: %Angle{r: 0.982793723247329}}
""" """
@spec to_spherical(Cartesian.t) :: Sperical.t @spec to_spherical(Cartesian.t()) :: Sperical.t()
def to_spherical(%Cartesian{x: x, y: y, z: z}) do def to_spherical(%Cartesian{x: x, y: y, z: z}) do
rho = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) rho = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
theta = Radian.init(acos(z / rho)) theta = Radian.init(acos(z / rho))

View file

@ -20,7 +20,7 @@ defimpl Kinemat.Point, for: Kinemat.Cylindrical do
radial: 3, radial: 3,
vertical: 9} vertical: 9}
""" """
@spec to_cylindrical(Cylindrical.t) :: Cylindrical.t @spec to_cylindrical(Cylindrical.t()) :: Cylindrical.t()
def to_cylindrical(point), do: point def to_cylindrical(point), do: point
@doc """ @doc """
@ -32,7 +32,7 @@ defimpl Kinemat.Point, for: Kinemat.Cylindrical do
...> |> Point.to_cartesian() ...> |> Point.to_cartesian()
%Kinemat.Cartesian{x: 2.954423259036624, y: 0.520944533000791, z: 9} %Kinemat.Cartesian{x: 2.954423259036624, y: 0.520944533000791, z: 9}
""" """
@spec to_cartesian(Cylindrical.t) :: Cartesian.t @spec to_cartesian(Cylindrical.t()) :: Cartesian.t()
def to_cartesian(%Cylindrical{radial: r, azimuth: theta, vertical: z}) do def to_cartesian(%Cylindrical{radial: r, azimuth: theta, vertical: z}) do
x = cos(theta) x = cos(theta)
y = sin(theta) y = sin(theta)
@ -51,7 +51,7 @@ defimpl Kinemat.Point, for: Kinemat.Cylindrical do
polar: ~a(20)d, polar: ~a(20)d,
radial: 5.0} radial: 5.0}
""" """
@spec to_spherical(Cylindrical.t) :: Spherical.t @spec to_spherical(Cylindrical.t()) :: Spherical.t()
def to_spherical(%Cylindrical{radial: r0, azimuth: theta, vertical: z}) do def to_spherical(%Cylindrical{radial: r0, azimuth: theta, vertical: z}) do
r1 = sqrt(pow(r0, 2) + pow(z, 2)) r1 = sqrt(pow(r0, 2) + pow(z, 2))
phi = theta phi = theta

View file

@ -18,7 +18,7 @@ defimpl Kinemat.Point, for: Kinemat.Spherical do
radial: 3.9999999999999996, radial: 3.9999999999999996,
vertical: 6.92820323027551} vertical: 6.92820323027551}
""" """
@spec to_cylindrical(Spherical.t) :: Cylindrical.t @spec to_cylindrical(Spherical.t()) :: Cylindrical.t()
def to_cylindrical(%Spherical{radial: r, azimuth: theta, polar: phi}) do def to_cylindrical(%Spherical{radial: r, azimuth: theta, polar: phi}) do
theta1 = theta theta1 = theta
theta = phi theta = phi
@ -40,7 +40,7 @@ defimpl Kinemat.Point, for: Kinemat.Spherical do
y: 3.554377592712286, y: 3.554377592712286,
z: 11.276311449430901} z: 11.276311449430901}
""" """
@spec to_cartesian(Spherical.t) :: Cartesian.t @spec to_cartesian(Spherical.t()) :: Cartesian.t()
def to_cartesian(%Spherical{radial: r, azimuth: theta, polar: phi}) do def to_cartesian(%Spherical{radial: r, azimuth: theta, polar: phi}) do
x = r * sin(theta) * cos(phi) x = r * sin(theta) * cos(phi)
y = r * sin(theta) * sin(phi) y = r * sin(theta) * sin(phi)
@ -60,6 +60,6 @@ defimpl Kinemat.Point, for: Kinemat.Spherical do
polar: ~a(60)d, polar: ~a(60)d,
radial: 12} radial: 12}
""" """
@spec to_spherical(Spherical.t) :: Spherical.t @spec to_spherical(Spherical.t()) :: Spherical.t()
def to_spherical(point), do: point def to_spherical(point), do: point
end end

View file

@ -6,10 +6,7 @@ defmodule Kinemat.Quaternion do
Describes an orientation using Quaternions. Describes an orientation using Quaternions.
""" """
@type t :: %Quaternion{w: Angle.t, @type t :: %Quaternion{w: Angle.t(), x: number, y: number, z: number}
x: number,
y: number,
z: number}
@doc """ @doc """
Create a Quaternion using `w`, `x`, `y` and `z` where Create a Quaternion using `w`, `x`, `y` and `z` where
@ -22,13 +19,13 @@ defmodule Kinemat.Quaternion do
iex> Quaternion.init(~a(90)d, 10, 20, 30) iex> Quaternion.init(~a(90)d, 10, 20, 30)
%Kinemat.Quaternion{w: ~a(90)d, x: 10, y: 20, z: 30} %Kinemat.Quaternion{w: ~a(90)d, x: 10, y: 20, z: 30}
""" """
@spec init(Angle.t, number, number, number) :: t @spec init(Angle.t(), number, number, number) :: t
def init(%Angle{} = w, x, y, z), do: %Quaternion{w: w, x: x, y: y, z: z} def init(%Angle{} = w, x, y, z), do: %Quaternion{w: w, x: x, y: y, z: z}
@doc """ @doc """
Returns the `w` component of the Quaternion. Returns the `w` component of the Quaternion.
""" """
@spec w(t) :: Angle.t @spec w(t) :: Angle.t()
def w(%Quaternion{w: w}), do: w def w(%Quaternion{w: w}), do: w
@doc """ @doc """

View file

@ -6,9 +6,9 @@ defmodule Kinemat.RotationMatrix do
Describes a 3 dimension orientation with a Rotation Matrix. Describes a 3 dimension orientation with a Rotation Matrix.
""" """
@type matrix3 :: {number, number, number, @type matrix3 ::
number, number, number, {number, number, number, number, number, number, number, number,
number, number, number} number}
@type t :: %RotationMatrix{matrix: matrix3} @type t :: %RotationMatrix{matrix: matrix3}
@doc """ @doc """
@ -22,10 +22,9 @@ defmodule Kinemat.RotationMatrix do
""" """
@spec init(matrix3) :: t @spec init(matrix3) :: t
def init({n0, n1, n2, n3, n4, n5, n6, n7, n8} = matrix) def init({n0, n1, n2, n3, n4, n5, n6, n7, n8} = matrix)
when is_number(n0) and is_number(n1) and is_number(n2) when is_number(n0) and is_number(n1) and is_number(n2) and is_number(n3) and
and is_number(n3) and is_number(n4) and is_number(n5) is_number(n4) and is_number(n5) and is_number(n6) and is_number(n7) and
and is_number(n6) and is_number(n7) and is_number(n8) is_number(n8) do
do
%RotationMatrix{matrix: matrix} %RotationMatrix{matrix: matrix}
end end

View file

@ -12,24 +12,20 @@ defmodule Kinemat.RotationMatrix.ToEuler do
@doc """ @doc """
Convert a RotationMatrix into an :xyz Euler. Convert a RotationMatrix into an :xyz Euler.
""" """
@spec to_euler(RotationMatrix.t) :: Euler.t @spec to_euler(RotationMatrix.t()) :: Euler.t()
def to_euler(orientation), do: to_euler(orientation, :xyz) def to_euler(orientation), do: to_euler(orientation, :xyz)
@doc """ @doc """
Convert a RotationMatrix into a Euler of the specified order. Convert a RotationMatrix into a Euler of the specified order.
""" """
@spec to_euler(RotationMatrix.t, Euler.valid_orientation) :: Euler.t @spec to_euler(RotationMatrix.t(), Euler.valid_orientation()) :: Euler.t()
def to_euler(%RotationMatrix{matrix: matrix}, representation) do def to_euler(%RotationMatrix{matrix: matrix}, representation) do
{x, y, z} = compute_euler(matrix, representation) {x, y, z} = compute_euler(matrix, representation)
Euler.init(representation, Euler.init(representation, Radian.init(x), Radian.init(y), Radian.init(z))
Radian.init(x),
Radian.init(y),
Radian.init(z))
end end
defp compute_euler({m00, m01, m02, _, _, m12, _, _, m22}, :xyz) defp compute_euler({m00, m01, m02, _, _, m12, _, _, m22}, :xyz)
when abs(m02) < 0.99999 when abs(m02) < 0.99999 do
do
y = m02 |> clamp |> asin() y = m02 |> clamp |> asin()
x = atan2(0 - m12, m22) x = atan2(0 - m12, m22)
z = atan2(0 - m01, m00) z = atan2(0 - m01, m00)
@ -121,5 +117,4 @@ defmodule Kinemat.RotationMatrix.ToEuler do
defp clamp(value) when value < -1, do: -1 defp clamp(value) when value < -1, do: -1
defp clamp(value) when value > 1, do: 1 defp clamp(value) when value > 1, do: 1
defp clamp(value), do: value defp clamp(value), do: value
end end

View file

@ -12,16 +12,17 @@ defmodule Kinemat.RotationMatrix.ToQuaternion do
Converts rotation matrices into quaternions. Converts rotation matrices into quaternions.
Used internally by the `Orientation` implementation. Used internally by the `Orientation` implementation.
""" """
@spec to_quaternion(RotationMatrix.t) :: Quaternion.t @spec to_quaternion(RotationMatrix.t()) :: Quaternion.t()
def to_quaternion(%RotationMatrix{matrix: {m00, _, _, _, m11, _, _, _, m22} = matrix}) do def to_quaternion(%RotationMatrix{
matrix: {m00, _, _, _, m11, _, _, _, m22} = matrix
}) do
trace = m00 + m11 + m22 trace = m00 + m11 + m22
{w, x, y, z} = build_quaternion(matrix, trace) {w, x, y, z} = build_quaternion(matrix, trace)
Quaternion.init(Radian.init(w), x, y, z) Quaternion.init(Radian.init(w), x, y, z)
end end
defp build_quaternion({_, m01, m02, m10, _, m12, m20, m21, _}, trace) defp build_quaternion({_, m01, m02, m10, _, m12, m20, m21, _}, trace)
when trace > 0 when trace > 0 do
do
s = 0.5 / sqrt(trace + 1.0) s = 0.5 / sqrt(trace + 1.0)
w = 0.25 / 2 w = 0.25 / 2
x = (m21 - m12) * s x = (m21 - m12) * s
@ -31,8 +32,7 @@ defmodule Kinemat.RotationMatrix.ToQuaternion do
end end
defp build_quaternion({m00, m01, m02, m10, m11, m12, m20, m21, m22}, _trace) defp build_quaternion({m00, m01, m02, m10, m11, m12, m20, m21, m22}, _trace)
when m00 > m11 and m00 > m22 when m00 > m11 and m00 > m22 do
do
s = 2.0 * sqrt(1.0 + m00 - m11 - m22) s = 2.0 * sqrt(1.0 + m00 - m11 - m22)
w = (m21 - m12) / s w = (m21 - m12) / s
x = 0.25 * s x = 0.25 * s
@ -42,8 +42,7 @@ defmodule Kinemat.RotationMatrix.ToQuaternion do
end end
defp build_quaternion({m00, m01, m02, m10, m11, m12, m20, m21, m22}, _trace) defp build_quaternion({m00, m01, m02, m10, m11, m12, m20, m21, m22}, _trace)
when m11 > m22 when m11 > m22 do
do
s = 2.0 * sqrt(1.0 + m11 - m00 - m22) s = 2.0 * sqrt(1.0 + m11 - m00 - m22)
w = (m02 - m20) / s w = (m02 - m20) / s
x = (m01 + m10) / s x = (m01 + m10) / s

View file

@ -6,21 +6,19 @@ defmodule Kinemat.Spherical do
Describes a point in 3D space using sperical notation (r,θ,ɸ). Describes a point in 3D space using sperical notation (r,θ,ɸ).
""" """
@type t :: %Spherical{radial: number, @type t :: %Spherical{radial: number, azimuth: Angle.t(), polar: Angle.t()}
azimuth: Angle.t,
polar: Angle.t}
@doc """ @doc """
Initialise a sperical coordinate point from `rho`, `theta` and `phi` (r,θ,ɸ). Initialise a sperical coordinate point from `rho`, `theta` and `phi` (r,θ,ɸ).
## Examples ## Examples
iex> Spherical.init(10, Degrees.init(20), Degrees.init(30)) iex> Spherical.init(10, ~a(20)d, ~a(30)d)
%Kinemat.Spherical{radial: 10, %Kinemat.Spherical{radial: 10,
azimuth: ~a(20)d, azimuth: ~a(20)d,
polar: ~a(30)d} polar: ~a(30)d}
""" """
@spec init(number, Angle.t, Angle.t) :: t @spec init(number, Angle.t(), Angle.t()) :: t
def init(rho, %Angle{} = theta, %Angle{} = phi) do def init(rho, %Angle{} = theta, %Angle{} = phi) do
%Spherical{radial: rho, azimuth: theta, polar: phi} %Spherical{radial: rho, azimuth: theta, polar: phi}
end end
@ -46,7 +44,7 @@ defmodule Kinemat.Spherical do
@doc """ @doc """
Alias for `azimuth/1`. Alias for `azimuth/1`.
""" """
@spec theta(t) :: Angle.t @spec theta(t) :: Angle.t()
def theta(point), do: point |> azimuth() def theta(point), do: point |> azimuth()
@doc """ @doc """
@ -57,14 +55,14 @@ defmodule Kinemat.Spherical do
iex> Spherical.init(10, ~a(20)d, ~a(30)d) iex> Spherical.init(10, ~a(20)d, ~a(30)d)
...> |> Spherical.azimuth ...> |> Spherical.azimuth
~a(20)d ~a(20)d
""" """
@spec azimuth(t) :: Angle.t @spec azimuth(t) :: Angle.t()
def azimuth(%Spherical{azimuth: theta}), do: theta def azimuth(%Spherical{azimuth: theta}), do: theta
@doc """ @doc """
Alias for `polar/1`. Alias for `polar/1`.
""" """
@spec phi(t) :: Angle.t @spec phi(t) :: Angle.t()
def phi(point), do: point |> polar() def phi(point), do: point |> polar()
@doc """ @doc """
@ -76,6 +74,6 @@ defmodule Kinemat.Spherical do
...> |> Spherical.polar ...> |> Spherical.polar
~a(30)d ~a(30)d
""" """
@spec polar(t) :: Angle.t @spec polar(t) :: Angle.t()
def polar(%Spherical{polar: phi}), do: phi def polar(%Spherical{polar: phi}), do: phi
end end

View file

@ -1,4 +1,8 @@
defmodule Kinemat.Trig do defmodule Kinemat.Trig do
@moduledoc """
A simple wrapper around `Angle`'s trig functions for compatibility.
"""
def sin(%Angle{} = angle) do def sin(%Angle{} = angle) do
{_, result} = Angle.sin(angle) {_, result} = Angle.sin(angle)
result result

View file

@ -11,7 +11,7 @@ defmodule Kinemat.Mixfile do
app: :kinemat, app: :kinemat,
version: @version, version: @version,
elixir: "~> 1.5", elixir: "~> 1.5",
start_permanent: Mix.env == :prod, start_permanent: Mix.env() == :prod,
package: package(), package: package(),
deps: deps(), deps: deps(),
description: @description description: @description
@ -20,8 +20,8 @@ defmodule Kinemat.Mixfile do
def package do def package do
[ [
maintainers: [ "James Harton <james@automat.nz>" ], maintainers: ["James Harton <james@automat.nz>"],
licenses: [ "MIT" ], licenses: ["MIT"],
links: %{ links: %{
"Source" => "https://gitlab.com/jimsy/kinemat" "Source" => "https://gitlab.com/jimsy/kinemat"
} }
@ -43,9 +43,8 @@ defmodule Kinemat.Mixfile do
{:credo, "~> 0.6", only: ~w(dev test)a, runtime: false}, {:credo, "~> 0.6", only: ~w(dev test)a, runtime: false},
{:inch_ex, "~> 0.5", only: ~w(dev test)a, runtime: false}, {:inch_ex, "~> 0.5", only: ~w(dev test)a, runtime: false},
{:dialyxir, "~> 0.5", only: ~w(dev test)a, runtime: false}, {:dialyxir, "~> 0.5", only: ~w(dev test)a, runtime: false},
{:graphmath, "~> 1.0"}, {:graphmath, "~> 1.0"},
{:angle, "~> 0.2"} {:angle, ">= 0.2.1"}
] ]
end end
end end

View file

@ -1,9 +1,11 @@
%{"angle": {:hex, :angle, "0.2.0", "2295005054c10dd9841ce657924de75109e1a9b364cc771c406b6d22f2693745", [], [], "hexpm"}, %{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [], [], "hexpm"}, "angle": {:hex, :angle, "0.2.1", "b2b5df5053b52a2fa65c9604a34570fda18a82c44fd3c7f9e83ce5acf881b4bf", [:mix], [], "hexpm"},
"credo": {:hex, :credo, "0.8.8", "990e7844a8d06ebacd88744a55853a83b74270b8a8461c55a4d0334b8e1736c9", [], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [], [], "hexpm"}, "credo": {:hex, :credo, "0.8.10", "261862bb7363247762e1063713bb85df2bbd84af8d8610d1272cd9c1943bba63", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
"graphmath": {:hex, :graphmath, "1.0.3", "ff961a5533fea4648ae47501ff241b4004ee719220d251abd44bc83f78f891bb", [], [], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "graphmath": {:hex, :graphmath, "1.0.3", "ff961a5533fea4648ae47501ff241b4004ee719220d251abd44bc83f78f891bb", [:mix], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"}} "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
}

View file

@ -11,18 +11,38 @@ defmodule Kinemat.Orientation.EulerToQuaternionTest do
# These values computed using the Mittsu Ruby gem. # These values computed using the Mittsu Ruby gem.
@computed %{ @computed %{
xyz: {0.9372468582005039, 0.2745997312243201, 0.13302686547026613, 0.168722160571667}, xyz:
yxz: {0.9489794544309479, 0.2745997312243201, 0.13302686547026613, 0.07960424450132775}, {0.9372468582005039, 0.2745997312243201, 0.13302686547026613,
zxy: {0.9372468582005039, 0.2308130859876117, 0.1995657251688989, 0.168722160571667}, 0.168722160571667},
zyx: {0.9489794544309479, 0.2308130859876117, 0.1995657251688989, 0.07960424450132775}, yxz:
yzx: {0.9372468582005039, 0.2745997312243201, 0.1995657251688989, 0.07960424450132775}, {0.9489794544309479, 0.2745997312243201, 0.13302686547026613,
xzy: {0.9489794544309479, 0.2308130859876117, 0.13302686547026613, 0.168722160571667} 0.07960424450132775},
zxy:
{0.9372468582005039, 0.2308130859876117, 0.1995657251688989,
0.168722160571667},
zyx:
{0.9489794544309479, 0.2308130859876117, 0.1995657251688989,
0.07960424450132775},
yzx:
{0.9372468582005039, 0.2745997312243201, 0.1995657251688989,
0.07960424450132775},
xzy:
{0.9489794544309479, 0.2308130859876117, 0.13302686547026613,
0.168722160571667}
} }
Enum.each(@computed, fn {representation, {w, x, y, z}} -> Enum.each(@computed, fn {representation, {w, x, y, z}} ->
test "convert #{representation} Euler into Quaternion" do test "convert #{representation} Euler into Quaternion" do
euler = Euler.init(unquote(representation), @deg_30, @deg_20, @deg_15) euler = Euler.init(unquote(representation), @deg_30, @deg_20, @deg_15)
q0 = Quaternion.init(Radian.init(unquote(w)), unquote(x), unquote(y), unquote(z))
q0 =
Quaternion.init(
Radian.init(unquote(w)),
unquote(x),
unquote(y),
unquote(z)
)
q1 = to_quaternion(euler) q1 = to_quaternion(euler)
assert q0 == q1 assert q0 == q1
end end

View file

@ -10,35 +10,43 @@ defmodule Kinemat.Orientation.EulerToRotationMatrixTest do
# These results were computed using the Mittsu ruby gem. # These results were computed using the Mittsu ruby gem.
@computed_results %{ @computed_results %{
xyz: { 0.9076733711903687, 0.3893269128166894, -0.15669590354740334, xyz:
{0.9076733711903687, 0.3893269128166894, -0.15669590354740334,
-0.24321034680169396, 0.7922556402871195, 0.5596246310178333, -0.24321034680169396, 0.7922556402871195, 0.5596246310178333,
0.3420201433256687, -0.46984631039295416, 0.8137976813493738}, 0.3420201433256687, -0.46984631039295416, 0.8137976813493738},
yxz: { 0.9519340346410572, 0.2241438680420134, -0.20876091614850517, yxz:
{0.9519340346410572, 0.2241438680420134, -0.20876091614850517,
-0.07802730202701791, 0.8365163037378079, 0.5423580124965611, -0.07802730202701791, 0.8365163037378079, 0.5423580124965611,
0.29619813272602386, -0.49999999999999994, 0.8137976813493738}, 0.29619813272602386, -0.49999999999999994, 0.8137976813493738},
zxy: { 0.8634127077396803, 0.40839339157637, -0.29619813272602386, zxy:
{0.8634127077396803, 0.40839339157637, -0.29619813272602386,
-0.2241438680420134, 0.8365163037378079, 0.49999999999999994, -0.2241438680420134, 0.8365163037378079, 0.49999999999999994,
0.4519712629501991, -0.36531535869380743, 0.8137976813493738}, 0.4519712629501991, -0.36531535869380743, 0.8137976813493738},
zyx: { 0.9076733711903687, 0.24321034680169396, -0.3420201433256687, zyx:
{0.9076733711903687, 0.24321034680169396, -0.3420201433256687,
-0.05896082326733734, 0.8807769671884964, 0.46984631039295416, -0.05896082326733734, 0.8807769671884964, 0.46984631039295416,
0.415514948649924, -0.40630119527123487, 0.8137976813493738}, 0.415514948649924, -0.40630119527123487, 0.8137976813493738},
yzx: { 0.9076733711903687, 0.25881904510252074, -0.33036608954935215, yzx:
{0.9076733711903687, 0.25881904510252074, -0.33036608954935215,
-0.03961626713065605, 0.8365163037378079, 0.5465080282662533, -0.03961626713065605, 0.8365163037378079, 0.5465080282662533,
0.41780330612687083, -0.4829629131445341, 0.7695370178986853}, 0.41780330612687083, -0.4829629131445341, 0.7695370178986853},
xzy: { 0.9076733711903687, 0.3816364104563247, -0.17459295932517688, xzy:
{0.9076733711903687, 0.3816364104563247, -0.17459295932517688,
-0.25881904510252074, 0.8365163037378079, 0.4829629131445341, -0.25881904510252074, 0.8365163037378079, 0.4829629131445341,
0.33036608954935215, -0.393184592519655, 0.8580583448000623} 0.33036608954935215, -0.393184592519655, 0.8580583448000623}
} }
Enum.each(@computed_results, fn Enum.each(@computed_results, fn {orientation,
{orientation, {m00, m01, m02, m10, m11, m12, m20, m21, m22}} -> {m00, m01, m02, m10, m11, m12, m20, m21, m22}} ->
test "converting from #{orientation} Euler to rotation matrix" do test "converting from #{orientation} Euler to rotation matrix" do
euler = Euler.init(unquote(orientation), @deg_30, @deg_20, @deg_15) euler = Euler.init(unquote(orientation), @deg_30, @deg_20, @deg_15)
computed = to_rotation_matrix(euler) computed = to_rotation_matrix(euler)
correct = {unquote(m00), unquote(m01), unquote(m02),
unquote(m10), unquote(m11), unquote(m12), correct =
unquote(m20), unquote(m21), unquote(m22)} {unquote(m00), unquote(m01), unquote(m02), unquote(m10), unquote(m11),
unquote(m12), unquote(m20), unquote(m21), unquote(m22)}
|> RotationMatrix.init() |> RotationMatrix.init()
assert computed == correct assert computed == correct
end end
end) end)

View file

@ -18,67 +18,79 @@ defmodule KinematPointTest do
test "cartesian -> cylindrical -> cartesian is correct" do test "cartesian -> cylindrical -> cartesian is correct" do
point = Cartesian.init(3, 4, 7) point = Cartesian.init(3, 4, 7)
result = point
result =
point
|> Point.to_cylindrical() |> Point.to_cylindrical()
|> Point.to_cartesian() |> Point.to_cartesian()
pretty_much_equal point.x, result.x pretty_much_equal(point.x, result.x)
pretty_much_equal point.y, result.y pretty_much_equal(point.y, result.y)
pretty_much_equal point.z, result.z pretty_much_equal(point.z, result.z)
end end
test "cartesian -> spherical -> cartesian is correct" do test "cartesian -> spherical -> cartesian is correct" do
point = Cartesian.init(3, 4, 7) point = Cartesian.init(3, 4, 7)
result = point
result =
point
|> Point.to_spherical() |> Point.to_spherical()
|> Point.to_cartesian() |> Point.to_cartesian()
pretty_much_equal point.x, result.x pretty_much_equal(point.x, result.x)
pretty_much_equal point.y, result.y pretty_much_equal(point.y, result.y)
pretty_much_equal point.z, result.z pretty_much_equal(point.z, result.z)
end end
test "cylindrical -> cartesian -> cylindrical" do test "cylindrical -> cartesian -> cylindrical" do
point = Cylindrical.init(30, ~a(10)d, 9) point = Cylindrical.init(30, ~a(10)d, 9)
result = point
result =
point
|> Point.to_cartesian() |> Point.to_cartesian()
|> Point.to_cylindrical() |> Point.to_cylindrical()
pretty_much_equal point.radial, result.radial pretty_much_equal(point.radial, result.radial)
pretty_much_equal point.azimuth, result.azimuth pretty_much_equal(point.azimuth, result.azimuth)
pretty_much_equal point.vertical, result.vertical pretty_much_equal(point.vertical, result.vertical)
end end
test "cylindrical -> spherical -> cylindrical" do test "cylindrical -> spherical -> cylindrical" do
point = Cylindrical.init(30, ~a(10)d, 9) point = Cylindrical.init(30, ~a(10)d, 9)
result = point
result =
point
|> Point.to_spherical() |> Point.to_spherical()
|> Point.to_cylindrical() |> Point.to_cylindrical()
pretty_much_equal point.radial, result.radial pretty_much_equal(point.radial, result.radial)
pretty_much_equal point.azimuth, result.azimuth pretty_much_equal(point.azimuth, result.azimuth)
pretty_much_equal point.vertical, result.vertical pretty_much_equal(point.vertical, result.vertical)
end end
test "spherical -> cartesian -> spherical" do test "spherical -> cartesian -> spherical" do
point = Spherical.init(30, ~a(20)d, ~a(60)d) point = Spherical.init(30, ~a(20)d, ~a(60)d)
result = point
result =
point
|> Point.to_cartesian() |> Point.to_cartesian()
|> Point.to_spherical() |> Point.to_spherical()
pretty_much_equal point.radial, result.radial pretty_much_equal(point.radial, result.radial)
pretty_much_equal point.azimuth, result.azimuth pretty_much_equal(point.azimuth, result.azimuth)
pretty_much_equal point.polar, result.polar pretty_much_equal(point.polar, result.polar)
end end
test "spherical -> cylindrical -> spherical" do test "spherical -> cylindrical -> spherical" do
point = Spherical.init(30, ~a(20)d, ~a(60)d) point = Spherical.init(30, ~a(20)d, ~a(60)d)
result = point
result =
point
|> Point.to_cylindrical() |> Point.to_cylindrical()
|> Point.to_spherical() |> Point.to_spherical()
pretty_much_equal point.radial, result.radial pretty_much_equal(point.radial, result.radial)
pretty_much_equal point.azimuth, result.azimuth pretty_much_equal(point.azimuth, result.azimuth)
pretty_much_equal point.polar, result.polar pretty_much_equal(point.polar, result.polar)
end end
end end

View file

@ -16,14 +16,21 @@ defmodule Kinemat.RotationMatrixToEulerTest do
xzy: {3.141592653589793, 0.0, -1.5707963267948966} xzy: {3.141592653589793, 0.0, -1.5707963267948966}
} }
Enum.each(@computed_results, fn Enum.each(@computed_results, fn {orientation, {x, y, z}} ->
{orientation, {x, y, z}} -> upcased = orientation |> to_string() |> String.upcase()
test "converting to #{orientation} Euler" do
computed = RotationMatrix.ToEuler.to_euler(@rotation, unquote(orientation)) test "converting to #{upcased} Euler" do
correct = Euler.init(unquote(orientation), computed =
RotationMatrix.ToEuler.to_euler(@rotation, unquote(orientation))
correct =
Euler.init(
unquote(orientation),
Radian.init(unquote(x)), Radian.init(unquote(x)),
Radian.init(unquote(y)), Radian.init(unquote(y)),
Radian.init(unquote(z))) Radian.init(unquote(z))
)
assert computed == correct assert computed == correct
end end
end) end)

View file

@ -6,7 +6,12 @@ defmodule Kinemat.RotationMatrixToQuaternionTest do
@rotation RotationMatrix.init({0, 1, 0, 1, 0, 0, 0, 0, -1}) @rotation RotationMatrix.init({0, 1, 0, 1, 0, 0, 0, 0, -1})
# This value calculated with the Mittsu Ruby gem. # This value calculated with the Mittsu Ruby gem.
@correct Quaternion.init(~a(0.0)r, 0.7071067811865475, 0.7071067811865476, 0.0) @correct Quaternion.init(
~a(0.0)r,
0.7071067811865475,
0.7071067811865476,
0.0
)
test "convert rotation matrix to quaternion" do test "convert rotation matrix to quaternion" do
computed = RotationMatrix.ToQuaternion.to_quaternion(@rotation) computed = RotationMatrix.ToQuaternion.to_quaternion(@rotation)