Skip to content

Commit

Permalink
Add triangle shape (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
alcarney authored Jun 25, 2020
1 parent 8f3aa85 commit 041b46a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 22 deletions.
64 changes: 64 additions & 0 deletions arlunio/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,67 @@ def T(x: X, y: Y, *, t0=0):
"""
t = np.arctan2(y, x)
return t - t0


@ar.definition
def Barycentric(x: X, y: Y, *, a=(0.5, -0.5), b=(0, 0.5), c=(-0.5, -0.5)):
"""Barycentric coordinates.
.. arlunio-image : : Barycentric Demo
:align: right
::
import arlunio.math as math
import arlunio.image as img
import numpy as np
coords = math.Barycentric()(width=256, height=256)
l1 = np.abs(coords[:, :, 0])
l2 = np.abs(coords[:, :, 1])
l3 = np.abs(coords[:, :, 2])
r = img.colorramp(l1, start='red', stop='black')
g = img.colorramp(l2, start='green', stop='black')
b = img.colorramp(l3, start='blue', stop='black')
rs = np.asarray(r)
gs = np.asarray(g)
bs = np.asarray(b)
image = img.fromarray(rs + gs + bs)
Returns the Barycentric coordinate grid as defined by the given triangle.
Attributes
----------
a:
The cartesian :math:`(x, y)` coordinates of the point :math:`a`
b:
The cartesian :math:`(x, y)` coordinates of the point :math:`b`
c:
The cartesian :math:`(x, y)` coordinates of the point :math:`c`
"""

h, w = x.shape

# Construct the transform matrix T
t1 = a[0] - c[0]
t2 = b[0] - c[0]
t3 = a[1] - c[1]
t4 = b[1] - c[1]

# Compute the inverse determinant
d = 1 / ((t1 * t4) - (t2 * t3))
p = np.dstack([x, y]).reshape(w * h, 2) - c

p1 = p[:, 0]
p2 = p[:, 1]

# Compute the coordinates
l1 = d * ((p1 * t4) - (p2 * t2))
l2 = d * ((p2 * t1) - (p1 * t3))
l3 = 1 - l1 - l2

return np.dstack([l1, l2, l3])[0].reshape(h, w, 3)
68 changes: 46 additions & 22 deletions arlunio/shape.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import numpy as np

import arlunio as ar
from arlunio.mask import all_
from arlunio.mask import Mask
from arlunio.math import X
from arlunio.math import Y
import arlunio.mask as mask
import arlunio.math as math


@ar.definition
def Circle(x: X, y: Y, *, xc=0, yc=0, r=0.8, pt=None) -> Mask:
def Circle(x: math.X, y: math.Y, *, xc=0, yc=0, r=0.8, pt=None) -> mask.Mask:
"""
.. arlunio-image:: Basic Circle
:align: right
Expand Down Expand Up @@ -124,16 +122,16 @@ def OlympicRings(width: int, height: int, *, spacing=0.5, pt=0.025):
circle = np.sqrt(x + y)

if pt is None:
return Mask(circle < r ** 2)
return mask.Mask(circle < r ** 2)

inner = (1 - pt) * r ** 2
outer = (1 + pt) * r ** 2

return all_(inner < circle, circle < outer)
return mask.all_(inner < circle, circle < outer)


@ar.definition
def Ellipse(x: X, y: Y, *, xc=0, yc=0, a=2, b=1, r=0.8, pt=None) -> Mask:
def Ellipse(x: math.X, y: math.Y, *, xc=0, yc=0, a=2, b=1, r=0.8, pt=None) -> mask.Mask:
"""
.. arlunio-image:: Simple Ellipse
:align: right
Expand Down Expand Up @@ -259,18 +257,18 @@ def Atom(x: X, y: Y):
ellipse = np.sqrt(x / a + y / b)

if pt is None:
return Mask(ellipse < r * r)
return mask.Mask(ellipse < r * r)

inner = (1 - pt) * r ** 2
outer = (1 + pt) * r ** 2

return all_(inner < ellipse, ellipse < outer)
return mask.all_(inner < ellipse, ellipse < outer)


@ar.definition
def SuperEllipse(
x: X, y: Y, *, xc=0, yc=0, a=1, b=1, n=3, r=0.8, m=None, pt=None
) -> Mask:
x: math.X, y: math.Y, *, xc=0, yc=0, a=1, b=1, n=3, r=0.8, m=None, pt=None
) -> mask.Mask:
"""
.. arlunio-image:: SuperEllipse
:align: right
Expand Down Expand Up @@ -406,16 +404,16 @@ def Sauron(width: int, height: int):
ellipse = np.abs(x / a) ** n + np.abs(y / b) ** m

if pt is None:
return Mask(ellipse < r)
return mask.Mask(ellipse < r)

inner = (1 - pt) * r
outer = (1 + pt) * r

return all_(inner < ellipse, ellipse < outer)
return mask.all_(inner < ellipse, ellipse < outer)


@ar.definition
def Square(x: X, y: Y, *, xc=0, yc=0, size=0.8, pt=None) -> Mask:
def Square(x: math.X, y: math.Y, *, xc=0, yc=0, size=0.8, pt=None) -> mask.Mask:
"""
.. arlunio-image:: Simple Square
:align: right
Expand Down Expand Up @@ -481,19 +479,21 @@ def SquareDemo(x: X, y: Y):
ys = np.abs(y - yc)

if pt is None:
return all_(xs < size, ys < size)
return mask.all_(xs < size, ys < size)

s = (1 - pt) * size
S = (1 + pt) * size

inner = all_(xs < s, ys < s)
outer = all_(xs < S, ys < S)
inner = mask.all_(xs < s, ys < s)
outer = mask.all_(xs < S, ys < S)

return outer - inner


@ar.definition
def Rectangle(x: X, y: Y, *, xc=0, yc=0, size=0.6, ratio=1.618, pt=None) -> Mask:
def Rectangle(
x: math.X, y: math.Y, *, xc=0, yc=0, size=0.6, ratio=1.618, pt=None
) -> mask.Mask:
"""
.. arlunio-image:: Simple Rectangle
:align: right
Expand Down Expand Up @@ -560,12 +560,36 @@ def RectangleDemo(width: int, height: int):
width = height * ratio

if pt is None:
return all_(xs < width, ys < height)
return mask.all_(xs < width, ys < height)

w, W = (1 - pt) * width, (1 + pt) * width
h, H = (1 - pt) * height, (1 + pt) * height

inner = all_(xs < w, ys < h)
outer = all_(xs < W, ys < H)
inner = mask.all_(xs < w, ys < h)
outer = mask.all_(xs < W, ys < H)

return outer - inner


@ar.definition
def Triangle(p: math.Barycentric) -> mask.Mask:
"""A triangle.
.. arlunio-image:: Triangle Demo
:align: right
::
import arlunio.shape as shape
import arlunio.image as img
tri = shape.Triangle()
image = img.fill(tri(width=256, height=256))
"""

l1 = p[:, :, 0]
l2 = p[:, :, 1]
l3 = p[:, :, 2]

return mask.all_(0 < l1, l1 < 1, 0 < l2, l2 < 1, 0 < l3, l3 < 1)
2 changes: 2 additions & 0 deletions changes/187.stdlib.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add new definition :code:`arlunio.shape.Triangle`, also add
new :code:`arlunio.math.Barycentric` definition which serves as the Triangle's base.

0 comments on commit 041b46a

Please sign in to comment.