Module weavingspace.weave_matrices

Functions to generate the matrices summarising tiles of tileable repeating geometries that when repeated across a map area give the appearance of a woven surface composed of criss-crossing strands.

Implementation is based on ideas discussed in variously

  • Glassner, A. 2002. Digital weaving. 1. IEEE Computer Graphics and Applications 22 (6):108–118.
  • ———. 2003a. Digital weaving. 3. IEEE Computer Graphics and Applications 23 (2):80-83.
  • ———. 2003b. Digital weaving. 2. IEEE Computer Graphics and Applications 23 (1):77-90.

and (unpublished)

where weaving is shown to be a matrix multiplication of tie-up, threading and treadling matrices. An accessible introduction can be found at https://www.youtube.com/watch?v=oMOSiag3dxg

Functions

def get_pattern(tie_up: numpy.ndarray,
treadling: numpy.ndarray,
threading: numpy.ndarray,
warp_n: int,
weft_n: int,
rep: int = 1) ‑> numpy.ndarray
Expand source code
def get_pattern(tie_up:np.ndarray, treadling:np.ndarray, threading:np.ndarray,
        warp_n:int, weft_n:int, rep:int = 1) -> np.ndarray:
  """Returns a 0/1 encoded weave matrix. 
  
  Given tie_up, treadling and threading matrices. The following conditions 
  must be satisfied to avoid non-conformable matrix error:
  
    treadling.shape[1] == tie_up.shape[0]
    tie_up.shape[1] == threading.shape[0]
  
  The "twill", "random", "basket" and "plain" options should guarantee
  this, but the "this" option requires the user to make this happen
  If the warp_n and weft_n values are not factors of treadling.shape[0] and
  threading.shape[1] respectively, the output matrix will be repeated as
  needed to make this match

  Args:
    tie_up (np.ndarray): the tie up matrix.
    treadling (np.ndarray): the treadling matrix.
    threading (np.ndarray): the threading matrix.
    warp_n (int): the number of uniquely labelled warp threads.
    weft_n (int): the number of uniquely labelled weft threads.
    rep (int, optional): optional additional repetition of the output       
      applied in both directions. Defaults to 1.

  Returns:
    np.ndarray: _description_
  """    
  pattern = (treadling @ tie_up @ threading > 0) + 0
  rep_warp = reps_needed(warp_n, pattern.shape[1])
  rep_weft = reps_needed(weft_n, pattern.shape[0])
  return np.tile(pattern, (rep_weft[1] * rep, rep_warp[1] * rep))

Returns a 0/1 encoded weave matrix.

Given tie_up, treadling and threading matrices. The following conditions must be satisfied to avoid non-conformable matrix error:

treadling.shape[1] == tie_up.shape[0] tie_up.shape[1] == threading.shape[0]

The "twill", "random", "basket" and "plain" options should guarantee this, but the "this" option requires the user to make this happen If the warp_n and weft_n values are not factors of treadling.shape[0] and threading.shape[1] respectively, the output matrix will be repeated as needed to make this match

Args

tie_up : np.ndarray
the tie up matrix.
treadling : np.ndarray
the treadling matrix.
threading : np.ndarray
the threading matrix.
warp_n : int
the number of uniquely labelled warp threads.
weft_n : int
the number of uniquely labelled weft threads.
rep : int, optional
optional additional repetition of the output
applied in both directions. Defaults to 1.

Returns

np.ndarray
description
def get_weave_pattern_matrix(weave_type: str = 'plain',
n: int | tuple[int] = 2,
warp: list[str] | tuple[str] = ['a', 'b'],
weft: list[str] | tuple[str] = ['c', 'd'],
tie_up: numpy.ndarray = array([[0, 1, 1, 0], [0, 0, 1, 1], [1, 0, 0, 1], [1, 1, 0, 0]]),
tr: numpy.ndarray = None,
th: numpy.ndarray = None) ‑> numpy.ndarray
Expand source code
def get_weave_pattern_matrix(weave_type:str = "plain", 
               n:Union[int, tuple[int]] = 2, 
               warp:Union[list[str], tuple[str]] = ["a", "b"],
               weft:Union[list[str], tuple[str]] = ["c", "d"], 
               tie_up:np.ndarray = make_twill_matrix((2, 2)), 
               tr:np.ndarray = None, 
               th:np.ndarray = None) -> np.ndarray:
  """Returns encoded weave pattern matrix.
  
  See `_encode_biaxial_weave()` for the encoding.

  Allowed weave_types: "plain", "twill", "basket", and "this" (pass-thru).
  These are explained in the respective functions to which this function
  delegates construction of the base matrices before applying the encoding.

  Args:
    weave_type (str, optional): one of "plain", "twill", "basket" or
      "this". Defaults to "plain".
    n (Union[int,tuple[int]], optional): over under pattern. See 
      make_over_under_row() for details. Defaults to 2.
    warp (Union[list[str],tuple[str]], optional): list of labels for warp 
      strands. Defaults to ["a", "b"].
    weft (Union[list[str],tuple[str]], optional): list of labels for weft 
      strands. Defaults to ["c", "d"].
    tie_up (np.ndarray, optional): a weave pattern matrix to pass thru in 
      the "this" case. Defaults to make_twill_matrix((2, 2)).
    tr (np.ndarray, optional): treadling matrix for the "this" case.        
      Defaults to None.
    th (np.ndarray, optional): threading matrix for the "this" case.        
      Defaults to None.

  Returns:
    np.ndarray: encoded weave pattern matrix.
  """    
  warps = [-1 if c == "-" else i for i, c in enumerate(warp)]
  wefts = [-1 if c == "-" else i for i, c in enumerate(weft)]
  width = len(warp)
  height = len(weft)
  
  if weave_type == "plain":
    p = make_plain_pattern(warp_n = width, weft_n = height)
  elif weave_type == "twill":
    p = make_twill_pattern(n = n, warp_n = width, weft_n = height)
  elif weave_type == "basket":
    p = make_basket_pattern(n = n, warp_n = width, weft_n = height)
  else:
    p = make_this_pattern(tie_up = tie_up, threading = th, treadling = tr,
                warp_n = width, weft_n = height)
  nr, nc = p.shape
  warp_threads = np.array(warps * 
      reps_needed(nc, len(warps))[1] * nr).reshape((nr, nc))
  weft_threads = np.array(wefts * 
      reps_needed(nr, len(wefts))[1] * nc).reshape((nc, nr)).transpose()
  # encode to reflect missing threads
  return _encode_biaxial_weave(p, warp_threads, weft_threads)

Returns encoded weave pattern matrix.

See _encode_biaxial_weave() for the encoding.

Allowed weave_types: "plain", "twill", "basket", and "this" (pass-thru). These are explained in the respective functions to which this function delegates construction of the base matrices before applying the encoding.

Args

weave_type : str, optional
one of "plain", "twill", "basket" or "this". Defaults to "plain".
n : Union[int,tuple[int]], optional
over under pattern. See make_over_under_row() for details. Defaults to 2.
warp : Union[list[str],tuple[str]], optional
list of labels for warp strands. Defaults to ["a", "b"].
weft : Union[list[str],tuple[str]], optional
list of labels for weft strands. Defaults to ["c", "d"].
tie_up : np.ndarray, optional
a weave pattern matrix to pass thru in the "this" case. Defaults to make_twill_matrix((2, 2)).
tr : np.ndarray, optional
treadling matrix for the "this" case.
Defaults to None.
th : np.ndarray, optional
threading matrix for the "this" case.
Defaults to None.

Returns

np.ndarray
encoded weave pattern matrix.
def make_basket_matrix(n: int) ‑> numpy.ndarray
Expand source code
def make_basket_matrix(n:int) -> np.ndarray:
  """Returns a basket weave pattern matrix.

  Makes a matrix like
  
    1 1 0 0
    1 1 0 0
    0 0 1 1
    0 0 1 1
  
  where the repeat runs in each row are length n

  Args:
    n (int): dimension of the 'basket' over-under pattern.

  Returns:
    np.ndarray: 0/1 matrix in basket weave pattern.
  """    
  return np.array((([1] * n + [0] * n) * n) + 
          (([0] * n + [1] * n) * n)).reshape(n * 2, n * 2)

Returns a basket weave pattern matrix.

Makes a matrix like

1 1 0 0 1 1 0 0 0 0 1 1 0 0 1 1

where the repeat runs in each row are length n

Args

n : int
dimension of the 'basket' over-under pattern.

Returns

np.ndarray
0/1 matrix in basket weave pattern.
def make_basket_pattern(n: int = 2, warp_n: int = 2, weft_n: int = 2) ‑> numpy.ndarray
Expand source code
def make_basket_pattern(n:int = 2, warp_n:int = 2, 
            weft_n:int = 2) -> np.ndarray: 
  """Returns basket pattern matrix extended for warp and weft patterns.

  Args:
    n (int, optional): over under count. Defaults to 2.
    warp_n (int, optional): number of warp thread labels. Defaults to 2.
    weft_n (int, optional): number of weft thread labels. Defaults to 2.

  Returns:
    np.ndarray: _description_
  """    
  tie_up = make_basket_matrix(n)
  threading = np.diag(np.ones(tie_up.shape[0]))
  treadling = np.diag(np.ones(tie_up.shape[1]))
  return get_pattern(tie_up, treadling, threading, warp_n, weft_n)

Returns basket pattern matrix extended for warp and weft patterns.

Args

n : int, optional
over under count. Defaults to 2.
warp_n : int, optional
number of warp thread labels. Defaults to 2.
weft_n : int, optional
number of weft thread labels. Defaults to 2.

Returns

np.ndarray
description
def make_over_under_row(n: int | tuple[int]) ‑> list[int]
Expand source code
def make_over_under_row(n:Union[int,tuple[int]]) -> list[int]:
  """Returns a tuple of runs of 1s and 0s.

  Returns a tuple of runs of 1s and 0s per supplied n. 
  
  If n is an integer, returned tuple will be a series of n 1s followed by
  n 0s.
  
  If n is a tuple of odd length it will be repeated to produce an even
  length over-under sequence. This is required to avoid cases such as
  e.g, (1,2,3) -> 100111 which repeated is 1001111001111, i.e. a (2,4)
  pattern. Repeating it yields 100111011000 which is the requested pattern.

  Args:
    n (Union[int,tuple[int]]): requested over-under sequence. See details.

  Returns:
    list[int]: list of runs of 1s and 0s in the requested pattern. 
  """    
  over_under = n
  if type(over_under) == int:
    over_under = [n, n]
  elif len(n) % 2 != 0:
    over_under = n * 2
  x = 1
  row = []
  for y in over_under:
    row.extend([x] * y)
    x = 1 - x
  return row

Returns a tuple of runs of 1s and 0s.

Returns a tuple of runs of 1s and 0s per supplied n.

If n is an integer, returned tuple will be a series of n 1s followed by n 0s.

If n is a tuple of odd length it will be repeated to produce an even length over-under sequence. This is required to avoid cases such as e.g, (1,2,3) -> 100111 which repeated is 1001111001111, i.e. a (2,4) pattern. Repeating it yields 100111011000 which is the requested pattern.

Args

n : Union[int,tuple[int]]
requested over-under sequence. See details.

Returns

list[int]
list of runs of 1s and 0s in the requested pattern.
def make_plain_pattern(warp_n: int = 1, weft_n: int = 1) ‑> numpy.ndarray
Expand source code
def make_plain_pattern(warp_n:int = 1, weft_n:int = 1) -> np.ndarray:
  """Returns plain weave (checkerboard) matrix.

  Args:
    warp_n (int, optional): number of warp thread labels. Defaults to 1.
    weft_n (int, optional): number of weft thread labels. Defaults to 1.

  Returns:
    np.ndarray: 0/1 matrix where 1 = warp on top.
  """    
  return make_twill_pattern(n = 1, warp_n = warp_n, weft_n = weft_n)

Returns plain weave (checkerboard) matrix.

Args

warp_n : int, optional
number of warp thread labels. Defaults to 1.
weft_n : int, optional
number of weft thread labels. Defaults to 1.

Returns

np.ndarray
0/1 matrix where 1 = warp on top.
def make_this_pattern(tie_up: numpy.ndarray,
threading: numpy.ndarray = None,
treadling: numpy.ndarray = None,
warp_n: int = 1,
weft_n: int = 1) ‑> numpy.ndarray
Expand source code
def make_this_pattern(tie_up:np.ndarray, 
            threading:np.ndarray = None,
            treadling:np.ndarray = None,
            warp_n:int = 1, weft_n:int = 1) -> np.ndarray:
  """Pass through returns weave pattern matrix from supplied input.

  This is just a pass through function which applies suitable size identity. 
  Could try to enforce
  
    treadling.shape[1] == tie_up.shape[0] and 
    tie_up.shape[1] == threading.shape[0]
  
  but unsure what would be an appropriate way to do this...

  Args:
    tie_up (np.ndarray): desired weave pattern.
    threading (np.ndarray): desired threading pattern.
    treadling (np.ndarray): desired treadling pattern.
    warp_n (int, optional): number of warp thread labels. Defaults to 1.
    weft_n (int, optional): number of weft thread labels. Defaults to 1.

  Returns:
    np.ndarray: resulting 0/1 weave pattern matrix.
  """    
  threading = (np.diag(np.ones(tie_up.shape[0]))
         if threading is None
         else threading)
  treadling = (np.diag(np.ones(tie_up.shape[1]))
         if treadling is None
         else treadling)
  return get_pattern(tie_up, treadling, threading, warp_n, weft_n)

Pass through returns weave pattern matrix from supplied input.

This is just a pass through function which applies suitable size identity. Could try to enforce

treadling.shape[1] == tie_up.shape[0] and tie_up.shape[1] == threading.shape[0]

but unsure what would be an appropriate way to do this…

Args

tie_up : np.ndarray
desired weave pattern.
threading : np.ndarray
desired threading pattern.
treadling : np.ndarray
desired treadling pattern.
warp_n : int, optional
number of warp thread labels. Defaults to 1.
weft_n : int, optional
number of weft thread labels. Defaults to 1.

Returns

np.ndarray
resulting 0/1 weave pattern matrix.
def make_twill_matrix(over_under: int | tuple[int]) ‑> numpy.ndarray
Expand source code
def make_twill_matrix(over_under:Union[int, tuple[int]]) -> np.ndarray:
  """Makes a twill 0/1 matrix.

  Makes a matrix like
  
    1 1 0 0
    0 1 1 0
    0 0 1 1
    1 0 0 1
    
  where the repeat runs in each row are lengths returned by
  make_over_under_rown(n)

  Args:
    over_under (Union[int,tuple[int]]): over-under run specification. See
      make_over_under_row().

  Returns:
    np.ndarray: a matrix of 0s and 1s.
  """    
  row = make_over_under_row(over_under)
  d = len(row)
  out = []
  for i in range(d):
    row = wrap_row(row, 1)
    out.extend(row)
  return np.array(out).reshape(d, d)

Makes a twill 0/1 matrix.

Makes a matrix like

1 1 0 0 0 1 1 0 0 0 1 1 1 0 0 1

where the repeat runs in each row are lengths returned by make_over_under_rown(n)

Args

over_under : Union[int,tuple[int]]
over-under run specification. See make_over_under_row().

Returns

np.ndarray
a matrix of 0s and 1s.
def make_twill_pattern(n: int | tuple[int] = 2, warp_n: int = 2, weft_n: int = 2) ‑> numpy.ndarray
Expand source code
def make_twill_pattern(n:Union[int, tuple[int]] = 2, 
             warp_n:int = 2, weft_n:int = 2) -> np.ndarray:
  """Returns twill pattern matrix extended for warp and weft patterns.

  n is the number of over-unders. With n = 1 we get a plain weave.

  Args:
    n (Union[int,tuple[int]], optional): specifies over-under sequence in
      the weave. Defaults to 2.
    warp_n (int, optional): number of warp thread labels. Defaults to 2.
    weft_n (int, optional): number of weft thread labels. Defaults to 2.

  Returns:
    np.ndarray: _description_
  """    
  tie_up = make_twill_matrix(n)
  threading = np.diag(np.ones(tie_up.shape[0]))
  treadling = np.diag(np.ones(tie_up.shape[1]))
  return get_pattern(tie_up, treadling, threading, warp_n, weft_n)

Returns twill pattern matrix extended for warp and weft patterns.

n is the number of over-unders. With n = 1 we get a plain weave.

Args

n : Union[int,tuple[int]], optional
specifies over-under sequence in the weave. Defaults to 2.
warp_n : int, optional
number of warp thread labels. Defaults to 2.
weft_n : int, optional
number of weft thread labels. Defaults to 2.

Returns

np.ndarray
description
def reps_needed(x1: int, x2: int) ‑> tuple[int]
Expand source code
def reps_needed(x1:int, x2:int) -> tuple[int]:
  """Returns how many repetitions of sequences of length x1 and x2 are 
  needed to make a matched length sequence 

  Args:
    x1 (int): length of first sequence.
    x2 (int): length of second sequence.

  Returns:
    tuple[int]: (number of repeats of x1, number of repeats of x2).
  """  
  n = np.lcm(x1, x2)
  return tuple(i for i in (n // x1, n // x2))

Returns how many repetitions of sequences of length x1 and x2 are needed to make a matched length sequence

Args

x1 : int
length of first sequence.
x2 : int
length of second sequence.

Returns

tuple[int]
(number of repeats of x1, number of repeats of x2).
def wrap_row(r: list, by: int = 1) ‑> list
Expand source code
def wrap_row(r:list, by:int = 1) -> list:
  """Wraps the list r by number of positions.
  
  Positive by will shift right.
  Negative shifts left.

  Args:
    r (list): list to wrap.
    by (int, optional): number of positions to shift by. Defaults to 1.

  Returns:
    list: wrapped list.
  """    
  return r[-by:] + r[:-by]

Wraps the list r by number of positions.

Positive by will shift right. Negative shifts left.

Args

r : list
list to wrap.
by : int, optional
number of positions to shift by. Defaults to 1.

Returns

list
wrapped list.