Tiling patterns for 'XYZ function', using Tessagon 0.8. #104726
@ -7,7 +7,7 @@
|
||||
# dreampainter, cotejrp1, liero, Kayo Phoenix, sugiany, dommetysk, Jambay #
|
||||
# Phymec, Anthony D'Agostino, Pablo Vazquez, Richard Wilks, lijenstina, #
|
||||
# Sjaak-de-Draak, Phil Cote, cotejrp1, xyz presets by elfnor, revolt_randy, #
|
||||
# Vladimir Spivak (cwolf3d), #
|
||||
# Vladimir Spivak (cwolf3d), Chris Want #
|
||||
|
||||
|
||||
bl_info = {
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
# Original by Buerbaum Martin (Pontiac), Elod Csirmaz
|
||||
|
||||
import sys
|
||||
import os
|
||||
import bpy
|
||||
import bmesh
|
||||
import math
|
||||
import numpy
|
||||
from mathutils import *
|
||||
@ -15,8 +18,29 @@ from bpy.props import (
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
BoolProperty,
|
||||
EnumProperty
|
||||
)
|
||||
|
||||
# Default: look in current directory for embedded Tessagon
|
||||
TESSAGON_DIRECTORY = os.environ.get('TESSAGON_DIRECTORY',
|
||||
os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.append(TESSAGON_DIRECTORY)
|
||||
|
||||
from tessagon.core.tessagon_discovery import TessagonDiscovery
|
||||
from tessagon.adaptors.list_adaptor import ListAdaptor
|
||||
|
||||
# Create tiling menu with items in nice order
|
||||
find_tilings = TessagonDiscovery()
|
||||
tilings = find_tilings.with_classification('regular').to_list() + \
|
||||
find_tilings.with_classification('archimedean').to_list() + \
|
||||
find_tilings.with_classification('laves').to_list() + \
|
||||
find_tilings.with_classification('non_edge').to_list() + \
|
||||
find_tilings.with_classification('non_convex').to_list()
|
||||
|
||||
tiling_items = [('None', 'No tiling', 'No tiling')]
|
||||
for counter, tessagon in enumerate(tilings):
|
||||
name = tessagon.metadata.name
|
||||
tiling_items.append((str(counter), name, name))
|
||||
|
||||
# List of safe functions for eval()
|
||||
safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
|
||||
@ -48,7 +72,7 @@ safe_dict['min'] = min
|
||||
# new mesh (as used in from_pydata)
|
||||
# name ... Name of the new mesh (& object)
|
||||
|
||||
def create_mesh_object(context, verts, edges, faces, name):
|
||||
def create_mesh_object(context, verts, edges, faces, name, recalculate_normals):
|
||||
|
||||
# Create new mesh
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
@ -56,6 +80,12 @@ def create_mesh_object(context, verts, edges, faces, name):
|
||||
# Make a mesh from a list of verts/edges/faces
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
if recalculate_normals:
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bmesh.ops.recalc_face_normals(bm, faces = bm.faces)
|
||||
bm.to_mesh(mesh)
|
||||
|
||||
# Update mesh geometry after adding stuff
|
||||
mesh.update()
|
||||
|
||||
@ -228,7 +258,7 @@ class AddZFunctionSurface(Operator):
|
||||
|
||||
edgeloop_prev = edgeloop_cur
|
||||
|
||||
base = create_mesh_object(context, verts, [], faces, "Z Function")
|
||||
base = create_mesh_object(context, verts, [], faces, "Z Function", false)
|
||||
else:
|
||||
self.report({'WARNING'}, "Z Equation - No expression is given")
|
||||
|
||||
@ -240,7 +270,8 @@ class AddZFunctionSurface(Operator):
|
||||
def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
|
||||
range_u_min, range_u_max, range_u_step, wrap_u,
|
||||
range_v_min, range_v_max, range_v_step, wrap_v,
|
||||
a_eq, b_eq, c_eq, f_eq, g_eq, h_eq, n, close_v):
|
||||
a_eq, b_eq, c_eq, f_eq, g_eq, h_eq, n, close_v,
|
||||
tiling_type):
|
||||
|
||||
verts = []
|
||||
faces = []
|
||||
@ -305,17 +336,11 @@ def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
|
||||
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
return [], []
|
||||
|
||||
for vN in range(vRange):
|
||||
v = range_v_min + (vN * vStep)
|
||||
|
||||
for uN in range(uRange):
|
||||
u = range_u_min + (uN * uStep)
|
||||
|
||||
def eval_f(u, v):
|
||||
safe_dict['u'] = u
|
||||
safe_dict['v'] = v
|
||||
|
||||
safe_dict['n'] = n
|
||||
|
||||
# Try to evaluate the equations.
|
||||
try:
|
||||
safe_dict['a'] = float(eval(*expr_args_a))
|
||||
@ -325,16 +350,42 @@ def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
|
||||
safe_dict['g'] = float(eval(*expr_args_g))
|
||||
safe_dict['h'] = float(eval(*expr_args_h))
|
||||
|
||||
verts.append((
|
||||
float(eval(*expr_args_x)),
|
||||
return (float(eval(*expr_args_x)),
|
||||
float(eval(*expr_args_y)),
|
||||
float(eval(*expr_args_z))))
|
||||
float(eval(*expr_args_z)))
|
||||
|
||||
except:
|
||||
import traceback
|
||||
self.report({'WARNING'}, "Error evaluating expression(s) - "
|
||||
"Check the console for more info")
|
||||
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
|
||||
return None
|
||||
|
||||
if tiling_type != 'None':
|
||||
tessagon_class = tilings[int(tiling_type)]
|
||||
options = {
|
||||
'function': eval_f,
|
||||
'u_range': [range_u_min, range_u_max],
|
||||
'v_range': [range_v_min, range_v_max],
|
||||
'u_num': range_u_step,
|
||||
'v_num': range_v_step,
|
||||
'u_cyclic': wrap_u,
|
||||
'v_cyclic': wrap_v,
|
||||
'adaptor_class' : ListAdaptor
|
||||
}
|
||||
tessagon = tessagon_class(**options)
|
||||
hash = tessagon.create_mesh()
|
||||
return hash['vert_list'], hash['face_list']
|
||||
|
||||
for vN in range(vRange):
|
||||
v = range_v_min + (vN * vStep)
|
||||
|
||||
for uN in range(uRange):
|
||||
u = range_u_min + (uN * uStep)
|
||||
xyz = eval_f(u, v)
|
||||
if xyz is None:
|
||||
return [], []
|
||||
verts.append(xyz)
|
||||
|
||||
for vN in range(range_v_step):
|
||||
vNext = vN + 1
|
||||
@ -366,7 +417,6 @@ def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
|
||||
|
||||
return verts, faces
|
||||
|
||||
|
||||
# Original Script "Parametric.py" by Ed Mackey.
|
||||
# -> http://www.blinken.com/blender-plugins.php
|
||||
# Partly converted for Blender 2.5 by tuga3d.
|
||||
@ -470,6 +520,12 @@ class AddXYZFunctionSurface(Operator):
|
||||
"V values (only if U is wrapped)",
|
||||
default=False
|
||||
)
|
||||
tiling_type : EnumProperty(
|
||||
name='Tiling',
|
||||
description='Tiling type',
|
||||
items=tiling_items,
|
||||
default='None'
|
||||
)
|
||||
n_eq: IntProperty(
|
||||
name="Number of objects (n=0..N-1)",
|
||||
description="The parameter n will be the index "
|
||||
@ -511,7 +567,7 @@ class AddXYZFunctionSurface(Operator):
|
||||
show_wire : BoolProperty(
|
||||
name="Show wireframe",
|
||||
default=True,
|
||||
description="Add the object’s wireframe over solid drawing"
|
||||
description="Add the object's wireframe over solid drawing"
|
||||
)
|
||||
edit_mode : BoolProperty(
|
||||
name="Show in edit mode",
|
||||
@ -520,6 +576,9 @@ class AddXYZFunctionSurface(Operator):
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
tiling_type = self.tiling_type
|
||||
recalculate_normals = (tiling_type != 'None')
|
||||
|
||||
for n in range(0, self.n_eq):
|
||||
verts, faces = xyz_function_surface_faces(
|
||||
self,
|
||||
@ -541,12 +600,14 @@ class AddXYZFunctionSurface(Operator):
|
||||
self.g_eq,
|
||||
self.h_eq,
|
||||
n,
|
||||
self.close_v
|
||||
self.close_v,
|
||||
self.tiling_type
|
||||
)
|
||||
if not verts:
|
||||
return {'CANCELLED'}
|
||||
|
||||
obj = create_mesh_object(context, verts, [], faces, "XYZ Function")
|
||||
obj = create_mesh_object(context, verts, [], faces, "XYZ Function",
|
||||
recalculate_normals)
|
||||
|
||||
if self.show_wire:
|
||||
context.active_object.show_wire = True
|
||||
|
2
add_mesh_extra_objects/tessagon/__init__.py
Normal file
2
add_mesh_extra_objects/tessagon/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .version import __version__
|
||||
from .core.tessagon_discovery import TessagonDiscovery # noqa: F401
|
27
add_mesh_extra_objects/tessagon/adaptors/blender_adaptor.py
Normal file
27
add_mesh_extra_objects/tessagon/adaptors/blender_adaptor.py
Normal file
@ -0,0 +1,27 @@
|
||||
import bmesh
|
||||
|
||||
|
||||
class BlenderAdaptor:
|
||||
def __init__(self, **kwargs):
|
||||
self.bm = None
|
||||
|
||||
def create_empty_mesh(self):
|
||||
self.bm = bmesh.new()
|
||||
|
||||
def initialize_colors(self):
|
||||
pass
|
||||
|
||||
def create_vert(self, coords):
|
||||
return self.bm.verts.new(coords)
|
||||
|
||||
def create_face(self, verts):
|
||||
return self.bm.faces.new(verts)
|
||||
|
||||
def color_face(self, face, color_index):
|
||||
face.material_index = color_index
|
||||
|
||||
def finish_mesh(self):
|
||||
bmesh.ops.recalc_face_normals(self.bm, faces=self.bm.faces)
|
||||
|
||||
def get_mesh(self):
|
||||
return self.bm
|
33
add_mesh_extra_objects/tessagon/adaptors/list_adaptor.py
Normal file
33
add_mesh_extra_objects/tessagon/adaptors/list_adaptor.py
Normal file
@ -0,0 +1,33 @@
|
||||
class ListAdaptor:
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.vert_list = None
|
||||
self.face_list = None
|
||||
self.color_list = None
|
||||
|
||||
def create_empty_mesh(self):
|
||||
self.vert_list = []
|
||||
self.face_list = []
|
||||
self.color_list = []
|
||||
|
||||
def initialize_colors(self):
|
||||
self.color_list = [0]*len(self.face_list)
|
||||
|
||||
def create_vert(self, coords):
|
||||
self.vert_list.append(coords)
|
||||
return (len(self.vert_list) - 1)
|
||||
|
||||
def create_face(self, verts):
|
||||
self.face_list.append(verts)
|
||||
return (len(self.face_list) - 1)
|
||||
|
||||
def color_face(self, face, color_index):
|
||||
self.color_list[face] = color_index
|
||||
|
||||
def finish_mesh(self):
|
||||
pass
|
||||
|
||||
def get_mesh(self):
|
||||
return {'vert_list': self.vert_list,
|
||||
'face_list': self.face_list,
|
||||
'color_list': self.color_list}
|
48
add_mesh_extra_objects/tessagon/adaptors/vtk_adaptor.py
Normal file
48
add_mesh_extra_objects/tessagon/adaptors/vtk_adaptor.py
Normal file
@ -0,0 +1,48 @@
|
||||
import vtk
|
||||
|
||||
|
||||
class VtkAdaptor:
|
||||
def __init__(self, **kwargs):
|
||||
self.point_count = 0
|
||||
self.face_count = 0
|
||||
self.points = None
|
||||
self.polys = None
|
||||
self.poly_data = None
|
||||
self.scalars = None
|
||||
|
||||
def create_empty_mesh(self):
|
||||
self.pcoords = vtk.vtkFloatArray()
|
||||
self.pcoords.SetNumberOfComponents(3)
|
||||
self.points = vtk.vtkPoints()
|
||||
self.polys = vtk.vtkCellArray()
|
||||
self.poly_data = vtk.vtkPolyData()
|
||||
|
||||
def initialize_colors(self):
|
||||
self.scalars = vtk.vtkFloatArray()
|
||||
self.scalars.SetNumberOfComponents(1)
|
||||
self.scalars.SetNumberOfTuples(self.face_count)
|
||||
|
||||
def create_vert(self, coords):
|
||||
self.pcoords.InsertNextTuple3(*coords)
|
||||
index = self.point_count
|
||||
self.point_count += 1
|
||||
return index
|
||||
|
||||
def create_face(self, verts):
|
||||
self.polys.InsertNextCell(len(verts), verts)
|
||||
index = self.face_count
|
||||
self.face_count += 1
|
||||
return index
|
||||
|
||||
def color_face(self, face, color_index):
|
||||
self.scalars.SetTuple1(face, color_index)
|
||||
|
||||
def finish_mesh(self):
|
||||
self.points.SetData(self.pcoords)
|
||||
self.poly_data.SetPoints(self.points)
|
||||
self.poly_data.SetPolys(self.polys)
|
||||
if self.scalars:
|
||||
self.poly_data.GetCellData().SetScalars(self.scalars)
|
||||
|
||||
def get_mesh(self):
|
||||
return self.poly_data
|
22
add_mesh_extra_objects/tessagon/core/__init__.py
Normal file
22
add_mesh_extra_objects/tessagon/core/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
import re
|
||||
|
||||
# A couple of general purpose functions for manipulating
|
||||
# camel case class names and snake case function/method names.
|
||||
# (Mostly used for some questionable dynamic method creation ...).
|
||||
# TODO: setup demos to use this
|
||||
|
||||
|
||||
def class_name_to_method_name(class_name, prefix=''):
|
||||
method_name = prefix
|
||||
method_name += re.sub(r'(?<!^)(?=[A-Z])', '_', class_name).lower()
|
||||
return method_name
|
||||
|
||||
|
||||
def class_to_method_name(cls, prefix=''):
|
||||
# E.g., if cls is HexTessagon and prefix is 'whatever_',
|
||||
# this function returns 'whatever_hex_tessagon'
|
||||
return class_name_to_method_name(cls.__name__, prefix)
|
||||
|
||||
|
||||
def method_name_to_class_name(method_name):
|
||||
return ''.join(word.title() for word in method_name.split('_'))
|
236
add_mesh_extra_objects/tessagon/core/abstract_tile.py
Normal file
236
add_mesh_extra_objects/tessagon/core/abstract_tile.py
Normal file
@ -0,0 +1,236 @@
|
||||
from tessagon.core.value_blend import ValueBlend
|
||||
|
||||
|
||||
class AbstractTile(ValueBlend):
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
self.tessagon = tessagon
|
||||
self.f = tessagon.f
|
||||
|
||||
# Verts/faces indexed with 'left', 'right', 'center'
|
||||
self.u_symmetric = kwargs.get('u_symmetric', False)
|
||||
# Verts/faces indexed with 'bottom', 'middle', 'top'
|
||||
self.v_symmetric = kwargs.get('v_symmetric', False)
|
||||
|
||||
# Verts/faces with 'rotate' and a number.
|
||||
# Only rot_symmetric = 180 supported
|
||||
# TODO: implement rot_symmetric = 90 as needed
|
||||
self.rot_symmetric = kwargs.get('rot_symmetry', None)
|
||||
|
||||
self.id = None
|
||||
# This is not necessary to any of the calculations, just
|
||||
# makes debugging easier
|
||||
if 'id' in kwargs:
|
||||
self.id = kwargs['id']
|
||||
|
||||
# This is an identifier that is set by the generator (a tuple)
|
||||
# Really this should be merged with the id thingy above
|
||||
self.fingerprint = kwargs.get('fingerprint') or None
|
||||
|
||||
# Corners is list of tuples:
|
||||
# [bottomleft, bottomright, topleft, topright]
|
||||
self.corners = None
|
||||
self._init_corners(**kwargs)
|
||||
|
||||
self.neighbors = {
|
||||
'top': None,
|
||||
'bottom': None,
|
||||
'left': None,
|
||||
'right': None
|
||||
}
|
||||
|
||||
# Are the neighbors ordered backwards?
|
||||
# e.g., a tile with twist['right'] set to True:
|
||||
# self tile: right edge has v=0 at the bottom and v=1 at the top
|
||||
# right neighbor: left edge has v=1 at the bottom and v=0 at the top
|
||||
# (the right tile has twist['left'] true
|
||||
self.twist = {
|
||||
'top': False,
|
||||
'bottom': False,
|
||||
'left': False,
|
||||
'right': False
|
||||
}
|
||||
|
||||
def set_neighbors(self, **kwargs):
|
||||
if 'top' in kwargs:
|
||||
self.neighbors['top'] = kwargs['top']
|
||||
if 'bottom' in kwargs:
|
||||
self.neighbors['bottom'] = kwargs['bottom']
|
||||
if 'left' in kwargs:
|
||||
self.neighbors['left'] = kwargs['left']
|
||||
if 'right' in kwargs:
|
||||
self.neighbors['right'] = kwargs['right']
|
||||
|
||||
def get_neighbor_tile(self, neighbor_keys):
|
||||
tile = self
|
||||
for key in self._neighbor_path(neighbor_keys):
|
||||
if not tile.neighbors[key]:
|
||||
return None
|
||||
tile = tile.neighbors[key]
|
||||
return tile
|
||||
|
||||
@property
|
||||
def left(self):
|
||||
return self.get_neighbor_tile(["left"])
|
||||
|
||||
@property
|
||||
def right(self):
|
||||
return self.get_neighbor_tile(["right"])
|
||||
|
||||
@property
|
||||
def top(self):
|
||||
return self.get_neighbor_tile(["top"])
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return self.get_neighbor_tile(["bottom"])
|
||||
|
||||
def inspect(self, **kwargs):
|
||||
# For debugging topology
|
||||
if not self.id:
|
||||
return
|
||||
prefix = 'Tile'
|
||||
if 'tile_number' in kwargs:
|
||||
prefix += " #%s" % (kwargs['tile_number'])
|
||||
print("%s (%s):" % (prefix, self.__class__.__name__))
|
||||
print(" - self: %s" % (self.id))
|
||||
print(' - neighbors:')
|
||||
for key in ['top', 'left', 'right', 'bottom']:
|
||||
if self.neighbors[key]:
|
||||
tile = self.neighbors[key]
|
||||
if tile.id:
|
||||
print(" - %s" % (self._neighbor_str(key)))
|
||||
print(" - corners: (%2.4f, %2.4f) (%2.4f, %2.4f)" %
|
||||
tuple(self.corners[2] + self.corners[3]))
|
||||
print(" (%2.4f, %2.4f) (%2.4f, %2.4f)" %
|
||||
tuple(self.corners[0] + self.corners[1]))
|
||||
print(" - twist:", self.twist)
|
||||
if self.fingerprint:
|
||||
print(" - fingerprint:", self.fingerprint)
|
||||
print('')
|
||||
|
||||
# Below are protected
|
||||
|
||||
# A couple of abstract methods that will be useful for finding
|
||||
# and setting the vertices and faces on a tile
|
||||
def _get_nested_list_value(self, nested_list, index_keys):
|
||||
if not isinstance(index_keys, list):
|
||||
return nested_list[index_keys]
|
||||
value = nested_list
|
||||
for index in index_keys:
|
||||
value = value[index]
|
||||
|
||||
return value
|
||||
|
||||
def _set_nested_list_value(self, nested_list, index_keys, value):
|
||||
if not isinstance(index_keys, list):
|
||||
nested_list[index_keys] = value
|
||||
return
|
||||
reference = nested_list
|
||||
for index in index_keys[0:-1]:
|
||||
reference = reference[index]
|
||||
reference[index_keys[-1]] = value
|
||||
|
||||
def _neighbor_path(self, neighbor_keys):
|
||||
# Note: it is assumed that len(neighbor_keys) in [1, 2]
|
||||
# if len(neighbor_keys) == 1, the neighbor meets on an edge
|
||||
# if len(neighbor_keys) == 2, the neighbor meets at a corner,
|
||||
# and are diagonal for each other, e.g., ['left', 'top']
|
||||
# If the boundary is twisted, need to be careful because
|
||||
# left and become right, or top can become bottom on the
|
||||
# other side of the twisted boundary: try to traverse the
|
||||
# non-twisted boundary first to make the math easier
|
||||
if len(neighbor_keys) < 2:
|
||||
return neighbor_keys
|
||||
if self._should_twist_u(neighbor_keys):
|
||||
if (neighbor_keys[0] in ['top', 'bottom']):
|
||||
return [neighbor_keys[1], neighbor_keys[0]]
|
||||
elif self._should_twist_v(neighbor_keys):
|
||||
if (neighbor_keys[0] in ['left', 'right']):
|
||||
return [neighbor_keys[1], neighbor_keys[0]]
|
||||
return neighbor_keys
|
||||
|
||||
def _index_path(self, index_keys, neighbor_keys):
|
||||
path = index_keys
|
||||
if self._should_twist_u(neighbor_keys):
|
||||
path = self._u_flip(path)
|
||||
if self._should_twist_v(neighbor_keys):
|
||||
path = self._v_flip(path)
|
||||
return path
|
||||
|
||||
def _permute_value(self, index_keys, vals):
|
||||
# abstract function to permute values in a list
|
||||
# e.g., 'left' and 'right' in u_flip below
|
||||
if isinstance(index_keys, list):
|
||||
return [self._permute_value(u, vals) for u in index_keys]
|
||||
for i in range(len(vals)):
|
||||
if index_keys == vals[i]:
|
||||
return vals[(i+1) % len(vals)]
|
||||
|
||||
return index_keys
|
||||
|
||||
def _swap_value(self, index_keys, val1, val2):
|
||||
# abstract function to swap two values in a list
|
||||
# e.g., 'left' and 'right' in u_flip below
|
||||
return self._permute_value(index_keys, [val1, val2])
|
||||
|
||||
def _u_flip(self, index_keys):
|
||||
# swap each left with right (and vice versa) in list
|
||||
if not self.u_symmetric:
|
||||
return index_keys
|
||||
return self._swap_value(index_keys, 'left', 'right')
|
||||
|
||||
def _v_flip(self, index_keys):
|
||||
# swap each top with bottom (and vice versa) in list
|
||||
if not self.v_symmetric:
|
||||
return index_keys
|
||||
return self._swap_value(index_keys, 'bottom', 'top')
|
||||
|
||||
def _rotate_index(self, index_keys):
|
||||
# rotate
|
||||
if not self.rot_symmetric:
|
||||
return index_keys
|
||||
elif self.rot_symmetric == 180:
|
||||
keys = self._permute_value(index_keys, ['rotate0', 'rotate180'])
|
||||
keys = self._permute_value(keys, ['left', 'right'])
|
||||
keys = self._permute_value(keys, ['top', 'bottom'])
|
||||
return keys
|
||||
elif self.rot_symmetric == 90:
|
||||
return self._permute_value(index_keys,
|
||||
['rotate0', 'rotate90'
|
||||
'rotate180', 'rotate270'])
|
||||
|
||||
def _v_index(self, index_keys):
|
||||
# find either 'top' or 'bottom' in the list
|
||||
if ('bottom' in index_keys):
|
||||
return 'bottom'
|
||||
if ('top' in index_keys):
|
||||
return 'top'
|
||||
raise ValueError("no v_index found in %s" % (index_keys))
|
||||
|
||||
def _u_index(self, index_keys):
|
||||
# find either 'right' or 'left' in the list
|
||||
if ('left' in index_keys):
|
||||
return 'left'
|
||||
if ('right' in index_keys):
|
||||
return 'right'
|
||||
raise ValueError("no u_index found in %s" % (index_keys))
|
||||
|
||||
def _should_twist_u(self, neighbor_keys):
|
||||
# e.g., twist['bottom'] is True, and neigbor_keys has 'bottom' in it
|
||||
for twist in ['top', 'bottom']:
|
||||
if self.twist[twist] and twist in neighbor_keys:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _should_twist_v(self, neighbor_keys):
|
||||
# e.g., twist['left'] is True, and neigbor_keys has 'left' in it
|
||||
for twist in ['left', 'right']:
|
||||
if self.twist[twist] and twist in neighbor_keys:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _neighbor_str(self, key):
|
||||
tile = self.neighbors[key]
|
||||
if tile:
|
||||
return "%-9s%s" % ("%s:" % (key), tile.id)
|
||||
return "%s: None" % (key)
|
15
add_mesh_extra_objects/tessagon/core/alternating_tile.py
Normal file
15
add_mesh_extra_objects/tessagon/core/alternating_tile.py
Normal file
@ -0,0 +1,15 @@
|
||||
from tessagon.core.tile import Tile
|
||||
|
||||
|
||||
class AlternatingTile(Tile):
|
||||
def validate(self):
|
||||
this_tile_type = self.tile_type
|
||||
for name in self.neighbors:
|
||||
neighbor = self.neighbors[name]
|
||||
if neighbor and (neighbor.tile_type + this_tile_type != 1):
|
||||
raise ValueError("Tiles have bad parity "
|
||||
"(hint: maybe use an even number of tiles)")
|
||||
|
||||
@property
|
||||
def tile_type(self):
|
||||
return sum(self.fingerprint) % 2
|
83
add_mesh_extra_objects/tessagon/core/grid_tile_generator.py
Normal file
83
add_mesh_extra_objects/tessagon/core/grid_tile_generator.py
Normal file
@ -0,0 +1,83 @@
|
||||
from tessagon.core.tile_generator import TileGenerator
|
||||
|
||||
|
||||
class GridTileGenerator(TileGenerator):
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
|
||||
self.tiles = None
|
||||
|
||||
def initialize_tiles(self, **kwargs):
|
||||
tiles = [[None for i in range(self.v_num)] for j in range(self.u_num)]
|
||||
|
||||
for u in range(self.u_num):
|
||||
u_ratio0 = float(u) / self.u_num
|
||||
u_ratio1 = float(u + 1) / self.u_num
|
||||
v_shear0 = u * self.v_shear
|
||||
v_shear1 = (u + 1) * self.v_shear
|
||||
for v in range(self.v_num):
|
||||
v_ratio0 = float(v) / self.v_num
|
||||
v_ratio1 = float(v + 1) / self.v_num
|
||||
u_shear0 = v * self.u_shear
|
||||
u_shear1 = (v + 1) * self.u_shear
|
||||
corners = [self.blend(u_ratio0 + u_shear0 + self.u_phase,
|
||||
v_ratio0 + v_shear0 + self.v_phase),
|
||||
self.blend(u_ratio1 + u_shear0 + self.u_phase,
|
||||
v_ratio0 + v_shear1 + self.v_phase),
|
||||
self.blend(u_ratio0 + u_shear1 + self.u_phase,
|
||||
v_ratio1 + v_shear0 + self.v_phase),
|
||||
self.blend(u_ratio1 + u_shear1 + self.u_phase,
|
||||
v_ratio1 + v_shear1 + self.v_phase)]
|
||||
|
||||
tiles[u][v] = self.create_tile(u, v, corners, **kwargs)
|
||||
|
||||
self.tiles = tiles
|
||||
return tiles
|
||||
|
||||
def initialize_neighbors(self, **kwargs):
|
||||
tiles = self.tiles
|
||||
for u in range(self.u_num):
|
||||
u_prev = (u - 1) % self.u_num
|
||||
u_next = (u + 1) % self.u_num
|
||||
for v in range(self.v_num):
|
||||
v_prev = (v - 1) % self.v_num
|
||||
v_next = (v + 1) % self.v_num
|
||||
tile = tiles[u][v]
|
||||
|
||||
if not self.u_cyclic and u == 0:
|
||||
left = None
|
||||
elif self.v_twist and u == 0:
|
||||
left = tiles[u_prev][self.v_num - v - 1]
|
||||
tile.twist['left'] = True
|
||||
else:
|
||||
left = tiles[u_prev][v]
|
||||
|
||||
if not self.v_cyclic and v == self.v_num - 1:
|
||||
top = None
|
||||
elif self.u_twist and v == self.v_num - 1:
|
||||
top = tiles[self.u_num - u - 1][v_next]
|
||||
tile.twist['top'] = True
|
||||
else:
|
||||
top = tiles[u][v_next]
|
||||
|
||||
if not self.u_cyclic and u == self.u_num - 1:
|
||||
right = None
|
||||
elif self.v_twist and u == self.u_num - 1:
|
||||
right = tiles[u_next][self.v_num - v - 1]
|
||||
tile.twist['right'] = True
|
||||
else:
|
||||
right = tiles[u_next][v]
|
||||
|
||||
if not self.v_cyclic and v == 0:
|
||||
bottom = None
|
||||
elif self.u_twist and v == 0:
|
||||
bottom = tiles[self.u_num - u - 1][v_prev]
|
||||
tile.twist['bottom'] = True
|
||||
else:
|
||||
bottom = tiles[u][v_prev]
|
||||
|
||||
tile.set_neighbors(left=left, right=right, top=top,
|
||||
bottom=bottom)
|
||||
|
||||
def get_tiles(self):
|
||||
return [j for i in self.tiles for j in i]
|
@ -0,0 +1,198 @@
|
||||
from tessagon.core.tile_generator import TileGenerator
|
||||
|
||||
|
||||
class ParallelogramTileGenerator(TileGenerator):
|
||||
# This generates tiles that are rotated and combined
|
||||
# with a sheer transformation
|
||||
# (Turning a collection of tiles into a parallelogram.)
|
||||
# This is done so that the tile patterns can still be cyclic.
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
|
||||
# parallelogram_vectors is a pair of pairs, e.g, [[9,1], [-1, 3]]
|
||||
# This means:
|
||||
# * For one side of the paralellogram go 9 tiles across, 1 tile up
|
||||
# * From there, the next side you get by going -1 tiles across,
|
||||
# 3 tiles up
|
||||
# (Other sides are obvious. It's a parallelogram.)
|
||||
self.p = kwargs['parallelogram_vectors']
|
||||
self.determinant = \
|
||||
self.p[0][0] * self.p[1][1] - self.p[1][0] * self.p[0][1]
|
||||
self.validate_parallelogram()
|
||||
|
||||
# Rows
|
||||
self.inverse = [[self.p[1][1] / (self.determinant * self.u_num),
|
||||
-self.p[1][0] / (self.determinant * self.u_num)],
|
||||
[-self.p[0][1] / (self.determinant * self.v_num),
|
||||
self.p[0][0] / (self.determinant * self.v_num)]]
|
||||
|
||||
self.color_pattern = kwargs.get('color_pattern') or None
|
||||
|
||||
self.id_prefix = 'parallelogram_tiles'
|
||||
|
||||
# Mapped via a fingerprint
|
||||
self.tiles = {}
|
||||
|
||||
def validate_parallelogram(self):
|
||||
error = None
|
||||
if self.p[0][0] <= 0:
|
||||
error = "First parallelogram vector can't have negative u"
|
||||
elif self.p[1][1] <= 0:
|
||||
error = "Second parallelogram vector can't have negative v"
|
||||
if self.determinant == 0:
|
||||
error = "Parallelogram vector are colinear"
|
||||
elif self.determinant < 0:
|
||||
error = "First parallelogram vector is to the left of second"
|
||||
if error:
|
||||
raise ValueError(error)
|
||||
|
||||
def initialize_tiles(self):
|
||||
tiles = {}
|
||||
fingerprint_range = self.fingerprint_range()
|
||||
for u in fingerprint_range[0]:
|
||||
for v in fingerprint_range[1]:
|
||||
fingerprint = self.normalize_fingerprint(u, v)
|
||||
fingerprint_str = str(fingerprint)
|
||||
if fingerprint_str not in tiles:
|
||||
if self.valid_fingerprint(*fingerprint):
|
||||
tiles[fingerprint_str] = self.make_tile(*fingerprint)
|
||||
self.tiles = tiles
|
||||
return tiles
|
||||
|
||||
def initialize_neighbors(self):
|
||||
for tile in self.get_tiles():
|
||||
u = tile.fingerprint[0]
|
||||
v = tile.fingerprint[1]
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u - 1, v)
|
||||
fingerprint_str = str(fingerprint)
|
||||
left = self.tiles.get(fingerprint_str)
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u + 1, v)
|
||||
fingerprint_str = str(fingerprint)
|
||||
right = self.tiles.get(fingerprint_str)
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u, v - 1)
|
||||
fingerprint_str = str(fingerprint)
|
||||
bottom = self.tiles.get(fingerprint_str)
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u, v + 1)
|
||||
fingerprint_str = str(fingerprint)
|
||||
top = self.tiles.get(fingerprint_str)
|
||||
|
||||
tile.set_neighbors(left=left, right=right, top=top,
|
||||
bottom=bottom)
|
||||
|
||||
def get_tiles(self):
|
||||
return self.tiles.values()
|
||||
|
||||
def make_tile(self, *fingerprint):
|
||||
corners = self.make_corners(*fingerprint)
|
||||
return self.create_tile(fingerprint[0],
|
||||
fingerprint[1],
|
||||
corners)
|
||||
|
||||
def make_corners(self, *fingerprint):
|
||||
u = fingerprint[0]
|
||||
v = fingerprint[1]
|
||||
|
||||
return [
|
||||
self.make_corner(u, v),
|
||||
self.make_corner(u + 1, v),
|
||||
self.make_corner(u, v + 1),
|
||||
self.make_corner(u + 1, v + 1),
|
||||
]
|
||||
|
||||
def make_corner(self, u, v):
|
||||
c0 = self.inverse[0][0] * u + self.inverse[0][1] * v
|
||||
c1 = self.inverse[1][0] * u + self.inverse[1][1] * v
|
||||
return self.blend(c0, c1)
|
||||
|
||||
def valid_fingerprint(self, u, v):
|
||||
# Valid = all corners of tile with this fingerprint
|
||||
# are in the parallelogram (may be wrapped if cyclic)
|
||||
|
||||
# Assume u, v have been normalized already
|
||||
if not self.point_in_parallelogram(u, v):
|
||||
return False
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u + 1, v)
|
||||
if not self.point_in_parallelogram(*fingerprint):
|
||||
return False
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u, v + 1)
|
||||
if not self.point_in_parallelogram(*fingerprint):
|
||||
return False
|
||||
|
||||
fingerprint = self.normalize_fingerprint(u + 1, v + 1)
|
||||
if not self.point_in_parallelogram(*fingerprint):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def parallelogram_coord(self, u, v):
|
||||
# Convert to be in [0, self.u_num] x [0, self.v_num]
|
||||
# (ideally if in the parallelogram)
|
||||
|
||||
u_coord = (u * self.p[1][1] - v * self.p[1][0]) / self.determinant
|
||||
v_coord = (v * self.p[0][0] - u * self.p[0][1]) / self.determinant
|
||||
|
||||
return (u_coord, v_coord)
|
||||
|
||||
def point_in_parallelogram(self, u, v):
|
||||
parallelogram_uv = self.parallelogram_coord(u, v)
|
||||
|
||||
if 0.0 <= parallelogram_uv[0] <= self.u_num:
|
||||
if 0.0 <= parallelogram_uv[1] <= self.v_num:
|
||||
return True
|
||||
return False
|
||||
|
||||
def normalize_fingerprint(self, u, v):
|
||||
# Return a canonical fingerprint for tile with this fingerprint
|
||||
# Tiles that are essentually the same (due to cyclic/wrapping)
|
||||
# will have the same fingerprint.
|
||||
# The goal is to not create such tiles more than once
|
||||
while (True):
|
||||
u_old = u
|
||||
v_old = v
|
||||
parallelogram_uv = self.parallelogram_coord(u, v)
|
||||
if (self.u_cyclic):
|
||||
if parallelogram_uv[0] >= self.u_num:
|
||||
u -= (self.u_num * self.p[0][0])
|
||||
v -= (self.u_num * self.p[0][1])
|
||||
elif parallelogram_uv[0] < 0.0:
|
||||
u += (self.u_num * self.p[0][0])
|
||||
v += (self.u_num * self.p[0][1])
|
||||
|
||||
if (self.v_cyclic):
|
||||
if parallelogram_uv[1] >= self.v_num:
|
||||
u -= (self.v_num * self.p[1][0])
|
||||
v -= (self.v_num * self.p[1][1])
|
||||
elif parallelogram_uv[1] < 0.0:
|
||||
u += (self.v_num * self.p[1][0])
|
||||
v += (self.v_num * self.p[1][1])
|
||||
if u == u_old and v == v_old:
|
||||
return (u, v)
|
||||
|
||||
def fingerprint_range(self):
|
||||
# Maximum extents of what the ranges can be ...
|
||||
# (Then we can loop over these ranges and see if tiles
|
||||
# with these fingerprints are valid.)
|
||||
u_min = min(0,
|
||||
self.u_num * self.p[0][0],
|
||||
self.v_num * self.p[1][0],
|
||||
self.u_num * self.p[0][0] + self.v_num * self.p[1][0])
|
||||
u_max = max(0,
|
||||
self.u_num * self.p[0][0],
|
||||
self.v_num * self.p[1][0],
|
||||
self.u_num * self.p[0][0] + self.v_num * self.p[1][0])
|
||||
v_min = min(0,
|
||||
self.u_num * self.p[0][1],
|
||||
self.v_num * self.p[1][1],
|
||||
self.u_num * self.p[0][1] + self.v_num * self.p[1][1])
|
||||
v_max = max(0,
|
||||
self.u_num * self.p[0][1],
|
||||
self.v_num * self.p[1][1],
|
||||
self.u_num * self.p[0][1] + self.v_num * self.p[1][1])
|
||||
return (range(u_min, u_max), range(v_min, v_max))
|
161
add_mesh_extra_objects/tessagon/core/tessagon.py
Normal file
161
add_mesh_extra_objects/tessagon/core/tessagon.py
Normal file
@ -0,0 +1,161 @@
|
||||
from tessagon.core.grid_tile_generator import GridTileGenerator
|
||||
from tessagon.core.parallelogram_tile_generator \
|
||||
import ParallelogramTileGenerator
|
||||
|
||||
|
||||
class Tessagon:
|
||||
tile_class = None
|
||||
metadata = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if 'tile_generator' in kwargs:
|
||||
self.tile_generator = kwargs['tile_generator'](self, **kwargs)
|
||||
elif 'rot_factor' in kwargs:
|
||||
# Deprecated?
|
||||
rot_factor = kwargs['rot_factor']
|
||||
extra_args = {'parallelogram_vectors':
|
||||
[[rot_factor, -1], [1, rot_factor]]}
|
||||
|
||||
self.tile_generator = \
|
||||
ParallelogramTileGenerator(self,
|
||||
**{**kwargs, **extra_args})
|
||||
elif 'parallelogram_vectors' in kwargs:
|
||||
self.tile_generator = ParallelogramTileGenerator(self, **kwargs)
|
||||
else:
|
||||
self.tile_generator = GridTileGenerator(self, **kwargs)
|
||||
|
||||
self._initialize_function(**kwargs)
|
||||
|
||||
# Optional post processing function
|
||||
self.post_process = None
|
||||
if 'post_process' in kwargs:
|
||||
self.post_process = kwargs['post_process']
|
||||
|
||||
if 'adaptor_class' in kwargs:
|
||||
adaptor_class = kwargs['adaptor_class']
|
||||
self.mesh_adaptor = adaptor_class(**kwargs)
|
||||
else:
|
||||
raise ValueError('Must provide a mesh adaptor class')
|
||||
|
||||
self.color_pattern = kwargs.get('color_pattern') or None
|
||||
|
||||
self.tiles = None
|
||||
self.face_types = {}
|
||||
self.vert_types = {}
|
||||
|
||||
self._process_extra_parameters(**kwargs)
|
||||
|
||||
def create_mesh(self):
|
||||
self._initialize_tiles()
|
||||
|
||||
self.mesh_adaptor.create_empty_mesh()
|
||||
|
||||
self._calculate_verts()
|
||||
self._calculate_faces()
|
||||
|
||||
if self.color_pattern:
|
||||
self._calculate_colors()
|
||||
|
||||
self.mesh_adaptor.finish_mesh()
|
||||
|
||||
if self.post_process:
|
||||
# Run user defined post-processing code
|
||||
# Need to pass self here (this could be designed better)
|
||||
self.post_process(self)
|
||||
|
||||
return self.mesh_adaptor.get_mesh()
|
||||
|
||||
def inspect(self):
|
||||
print("\n=== %s ===\n" % (self.__class__.__name__))
|
||||
for i in range(len(self.tiles)):
|
||||
self.tiles[i].inspect(tile_number=i)
|
||||
|
||||
# Note, would like these to be a class properties,
|
||||
# but the designers of Python flip-flop about
|
||||
# how to implement it.
|
||||
@classmethod
|
||||
def num_color_patterns(cls):
|
||||
if cls.metadata is None:
|
||||
return 0
|
||||
return cls.metadata.num_color_patterns
|
||||
|
||||
@classmethod
|
||||
def num_extra_parameters(cls):
|
||||
if cls.metadata is None:
|
||||
return 0
|
||||
return len(cls.metadata.extra_parameters)
|
||||
|
||||
# Below are protected
|
||||
|
||||
def _initialize_function(self, **kwargs):
|
||||
self.f = None
|
||||
|
||||
if 'simple_2d' in kwargs:
|
||||
u_multiplier_2d = 1.0
|
||||
if self.metadata and self.metadata.uv_ratio:
|
||||
v_multiplier_2d = 1.0 / self.metadata.uv_ratio
|
||||
else:
|
||||
v_multiplier_2d = 1.0
|
||||
|
||||
tile_aspect = self.tile_generator.v_num / self.tile_generator.u_num
|
||||
multiplier_2d = kwargs.get('multiplier_2d', 1.0)
|
||||
u_multiplier_2d *= multiplier_2d
|
||||
v_multiplier_2d *= multiplier_2d * tile_aspect
|
||||
|
||||
translate_2d = kwargs.get('translate_2d', (0, 0))
|
||||
# Simple xy-plane
|
||||
self.f = lambda u, v: (translate_2d[0] + u_multiplier_2d * u,
|
||||
translate_2d[1] + v_multiplier_2d * v,
|
||||
0.0)
|
||||
|
||||
# Just to test how the corners are going to map ...
|
||||
# top_left = self.tile_generator.corners[0]
|
||||
# bottom_right = self.tile_generator.corners[3]
|
||||
# print(self.f(*top_left), self.f(*bottom_right))
|
||||
elif 'function' in kwargs:
|
||||
self.f = kwargs['function']
|
||||
else:
|
||||
raise ValueError('Must specify a function')
|
||||
|
||||
def _initialize_tiles(self):
|
||||
self.tiles = self.tile_generator.create_tiles()
|
||||
|
||||
def _calculate_verts(self):
|
||||
for tile in self.tiles:
|
||||
tile.calculate_verts()
|
||||
|
||||
def _calculate_faces(self):
|
||||
for tile in self.tiles:
|
||||
tile.calculate_faces()
|
||||
|
||||
def _calculate_colors(self):
|
||||
self.mesh_adaptor.initialize_colors()
|
||||
for tile in self.tiles:
|
||||
tile.calculate_colors()
|
||||
|
||||
def _process_extra_parameters(self, **kwargs):
|
||||
self.extra_parameters = {}
|
||||
parameters_info = self.metadata.extra_parameters
|
||||
if not parameters_info:
|
||||
return
|
||||
|
||||
for parameter in parameters_info:
|
||||
parameter_info = parameters_info[parameter]
|
||||
if parameter not in kwargs:
|
||||
continue
|
||||
value = kwargs.get(parameter)
|
||||
if parameter_info['type'] == 'float':
|
||||
self._process_float_extra_parameter(parameter,
|
||||
value,
|
||||
parameter_info)
|
||||
|
||||
def _process_float_extra_parameter(self, parameter, value, parameter_info):
|
||||
max_value = parameter_info.get('max')
|
||||
min_value = parameter_info.get('min')
|
||||
if max_value is not None and value > max_value:
|
||||
raise ValueError('Parameter {} ({}) exceeds maximum ({})'
|
||||
.format(parameter, value, max_value))
|
||||
if min_value is not None and value < min_value:
|
||||
raise ValueError('Parameter {} ({}) below minimum ({})'
|
||||
.format(parameter, value, min_value))
|
||||
self.extra_parameters[parameter] = value
|
116
add_mesh_extra_objects/tessagon/core/tessagon_discovery.py
Normal file
116
add_mesh_extra_objects/tessagon/core/tessagon_discovery.py
Normal file
@ -0,0 +1,116 @@
|
||||
from tessagon.types.hex_tessagon import HexTessagon
|
||||
from tessagon.types.tri_tessagon import TriTessagon
|
||||
from tessagon.types.octo_tessagon import OctoTessagon
|
||||
from tessagon.types.rhombus_tessagon import RhombusTessagon
|
||||
from tessagon.types.hex_tri_tessagon import HexTriTessagon
|
||||
from tessagon.types.hex_square_tri_tessagon import HexSquareTriTessagon
|
||||
from tessagon.types.square_tessagon import SquareTessagon
|
||||
from tessagon.types.pythagorean_tessagon import PythagoreanTessagon
|
||||
from tessagon.types.brick_tessagon import BrickTessagon
|
||||
from tessagon.types.dodeca_tessagon import DodecaTessagon
|
||||
from tessagon.types.square_tri_tessagon import SquareTriTessagon
|
||||
from tessagon.types.weave_tessagon import WeaveTessagon
|
||||
from tessagon.types.floret_tessagon import FloretTessagon
|
||||
from tessagon.types.hex_big_tri_tessagon import HexBigTriTessagon
|
||||
from tessagon.types.zig_zag_tessagon import ZigZagTessagon
|
||||
from tessagon.types.dissected_square_tessagon import DissectedSquareTessagon
|
||||
from tessagon.types.square_tri2_tessagon import SquareTri2Tessagon
|
||||
from tessagon.types.dodeca_tri_tessagon import DodecaTriTessagon
|
||||
from tessagon.types.dissected_triangle_tessagon \
|
||||
import DissectedTriangleTessagon
|
||||
from tessagon.types.dissected_hex_quad_tessagon \
|
||||
import DissectedHexQuadTessagon
|
||||
from tessagon.types.dissected_hex_tri_tessagon \
|
||||
import DissectedHexTriTessagon
|
||||
from tessagon.types.penta_tessagon import PentaTessagon
|
||||
from tessagon.types.penta2_tessagon import Penta2Tessagon
|
||||
from tessagon.types.big_hex_tri_tessagon import BigHexTriTessagon
|
||||
from tessagon.types.stanley_park_tessagon import StanleyParkTessagon
|
||||
from tessagon.types.valemount_tessagon import ValemountTessagon
|
||||
from tessagon.types.cloverdale_tessagon import CloverdaleTessagon
|
||||
from tessagon.types.islamic_hex_stars_tessagon import IslamicHexStarsTessagon
|
||||
from tessagon.types.islamic_stars_crosses_tessagon \
|
||||
import IslamicStarsCrossesTessagon
|
||||
|
||||
ALL = [SquareTessagon,
|
||||
HexTessagon,
|
||||
TriTessagon,
|
||||
|
||||
OctoTessagon,
|
||||
HexTriTessagon,
|
||||
HexSquareTriTessagon,
|
||||
DodecaTessagon,
|
||||
SquareTriTessagon,
|
||||
SquareTri2Tessagon,
|
||||
DodecaTriTessagon,
|
||||
BigHexTriTessagon,
|
||||
|
||||
RhombusTessagon,
|
||||
FloretTessagon,
|
||||
DissectedSquareTessagon,
|
||||
DissectedTriangleTessagon,
|
||||
DissectedHexQuadTessagon,
|
||||
DissectedHexTriTessagon,
|
||||
PentaTessagon,
|
||||
Penta2Tessagon,
|
||||
|
||||
PythagoreanTessagon,
|
||||
BrickTessagon,
|
||||
WeaveTessagon,
|
||||
HexBigTriTessagon,
|
||||
ZigZagTessagon,
|
||||
ValemountTessagon,
|
||||
CloverdaleTessagon,
|
||||
|
||||
StanleyParkTessagon,
|
||||
IslamicHexStarsTessagon,
|
||||
IslamicStarsCrossesTessagon]
|
||||
|
||||
|
||||
class TessagonDiscovery:
|
||||
def __init__(self, **kwargs):
|
||||
self.classes = kwargs.get('classes', ALL)
|
||||
|
||||
def count(self):
|
||||
return len(self.classes)
|
||||
|
||||
def to_list(self):
|
||||
return self.classes
|
||||
|
||||
def inverse(self):
|
||||
other_classes = list(set(ALL) - set(self.classes))
|
||||
return TessagonDiscovery(classes=other_classes)
|
||||
|
||||
def __add__(self, other):
|
||||
new_classes = list(set(self.classes) | set(other.classes))
|
||||
return TessagonDiscovery(classes=new_classes)
|
||||
|
||||
def __sub__(self, other):
|
||||
new_classes = list(set(self.classes) - set(other.classes))
|
||||
return TessagonDiscovery(classes=new_classes)
|
||||
|
||||
def with_color_patterns(self):
|
||||
results = []
|
||||
for klass in self.classes:
|
||||
if klass.metadata is None:
|
||||
continue
|
||||
if klass.metadata.has_color_patterns:
|
||||
results.append(klass)
|
||||
return TessagonDiscovery(classes=results)
|
||||
|
||||
def with_classification(self, classification):
|
||||
results = []
|
||||
for klass in self.classes:
|
||||
if klass.metadata is None:
|
||||
continue
|
||||
if klass.metadata.has_classification(classification):
|
||||
results.append(klass)
|
||||
return TessagonDiscovery(classes=results)
|
||||
|
||||
@classmethod
|
||||
def get_class(cls, class_name):
|
||||
if class_name in globals():
|
||||
klass = globals()[class_name]
|
||||
if klass in ALL:
|
||||
return klass
|
||||
raise ValueError(class_name + ' is not recognized by Tessagon')
|
61
add_mesh_extra_objects/tessagon/core/tessagon_metadata.py
Normal file
61
add_mesh_extra_objects/tessagon/core/tessagon_metadata.py
Normal file
@ -0,0 +1,61 @@
|
||||
class TessagonMetadata:
|
||||
CLASSIFICATION_MAP = {
|
||||
'regular': 'Regular tiling',
|
||||
'archimedean': 'Archimedean tiling',
|
||||
'laves': 'Laves tiling',
|
||||
'non_edge': 'Non-edge-to-edge tiling',
|
||||
'non_convex': 'Non-convex tiling'
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._name = kwargs.get('name')
|
||||
if not self._name:
|
||||
raise ValueError('No name set')
|
||||
self._num_color_patterns = kwargs.get('num_color_patterns', 0)
|
||||
self._classification = kwargs.get('classification', 'misc')
|
||||
self._shapes = kwargs.get('shapes', [])
|
||||
self._sides = kwargs.get('sides', [])
|
||||
self._uv_ratio = kwargs.get('uv_ratio', None)
|
||||
self._extra_parameters = kwargs.get('extra_parameters', {})
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def num_color_patterns(self):
|
||||
return self._num_color_patterns
|
||||
|
||||
@property
|
||||
def has_color_patterns(self):
|
||||
return self._num_color_patterns > 0
|
||||
|
||||
def has_shape(self, shape):
|
||||
if shape in self._shapes:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def classification(self):
|
||||
return self._classification
|
||||
|
||||
def has_classification(self, classification):
|
||||
return self._classification == classification
|
||||
|
||||
@property
|
||||
def human_readable_classification(self):
|
||||
return self.__class__.CLASSIFICATION_MAP[self._classification]
|
||||
|
||||
@property
|
||||
def uv_ratio(self):
|
||||
# Aspect ratio U/V for best looking proportions
|
||||
# Roughly, assuming a uniform input function,
|
||||
# we would want to select the u, v inputs so that:
|
||||
# (u_range[1] - u_range[0]) / u_num
|
||||
# = uv_ratio * (v_range[1] - v_range[0]) / v_num
|
||||
# Or scale the input function so get similar proportions.
|
||||
return self._uv_ratio
|
||||
|
||||
@property
|
||||
def extra_parameters(self):
|
||||
return self._extra_parameters
|
338
add_mesh_extra_objects/tessagon/core/tile.py
Normal file
338
add_mesh_extra_objects/tessagon/core/tile.py
Normal file
@ -0,0 +1,338 @@
|
||||
from tessagon.core.abstract_tile import AbstractTile
|
||||
|
||||
|
||||
class Tile(AbstractTile):
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
|
||||
self.mesh_adaptor = tessagon.mesh_adaptor
|
||||
|
||||
self.verts = self.init_verts()
|
||||
self.faces = self.init_faces()
|
||||
self.color_pattern = kwargs.get('color_pattern') or None
|
||||
if self.faces and self.color_pattern:
|
||||
self.face_paths = self.all_face_paths()
|
||||
|
||||
def validate(self):
|
||||
# Subclass decides if this should be done
|
||||
pass
|
||||
|
||||
def add_vert(self, index_keys, ratio_u, ratio_v, **kwargs):
|
||||
# Use the mesh adaptor to create a vertex.
|
||||
# In reality, multiple vertices may get defined if symmetry is declared
|
||||
vert = self._get_vert(index_keys)
|
||||
if vert is None:
|
||||
coords = self.f(*self.blend(ratio_u, ratio_v))
|
||||
vert = self.mesh_adaptor.create_vert(coords)
|
||||
|
||||
self._set_vert(index_keys, vert)
|
||||
if 'vert_type' in kwargs:
|
||||
if not kwargs['vert_type'] in self.tessagon.vert_types:
|
||||
self.tessagon.vert_types[kwargs['vert_type']] = []
|
||||
self.tessagon.vert_types[kwargs['vert_type']].append(vert)
|
||||
|
||||
if vert is not None:
|
||||
equivalent_verts = kwargs.get('equivalent', [])
|
||||
for equivalent_vert in equivalent_verts:
|
||||
self.set_equivalent_vert(*equivalent_vert, vert)
|
||||
|
||||
# We add additional vertices by flipping 'left', 'right' etc
|
||||
# if the tile has some kind of symmetry defined
|
||||
self._create_symmetric_verts(index_keys, ratio_u, ratio_v, **kwargs)
|
||||
|
||||
# On the boundary, make sure equivalent vertices are set on
|
||||
# neighbor tiles
|
||||
self._set_equivalent_neighbor_verts(index_keys, vert, **kwargs)
|
||||
|
||||
return vert
|
||||
|
||||
def set_equivalent_vert(self, neighbor_keys, index_keys, vert, **kwargs):
|
||||
# On boundary, the vert on a neighbor is equivalent to this vert
|
||||
# This is usually only called indirectly via add_vert, but check out
|
||||
# PythagoreanTile for an example of direct usage
|
||||
if vert is None:
|
||||
return None
|
||||
|
||||
tile = self.get_neighbor_tile(neighbor_keys)
|
||||
if tile is None:
|
||||
return None
|
||||
|
||||
tile._set_vert(self._index_path(index_keys, neighbor_keys), vert)
|
||||
|
||||
def add_face(self, index_keys, vert_index_keys_list, **kwargs):
|
||||
# Use the mesh adaptor to create a face.
|
||||
# In reality, multiple faces may get defined if symmetry is declared
|
||||
face = self._get_face(index_keys)
|
||||
|
||||
if face is None:
|
||||
verts = self._get_verts_from_list(vert_index_keys_list)
|
||||
if verts is not None:
|
||||
face = \
|
||||
self._make_face(index_keys, verts, **kwargs)
|
||||
|
||||
if face is not None:
|
||||
equivalent_faces = kwargs.get('equivalent', [])
|
||||
for equivalent_face in equivalent_faces:
|
||||
self.set_equivalent_face(*equivalent_face, face)
|
||||
|
||||
# We add additional faces by flipping 'left', 'right' etc
|
||||
# if the tile has some kind of symmetry defined
|
||||
self._create_symmetric_faces(index_keys, vert_index_keys_list,
|
||||
**kwargs)
|
||||
|
||||
return face
|
||||
|
||||
def _make_face(self, index_keys, verts, **kwargs):
|
||||
face = self.mesh_adaptor.create_face(verts)
|
||||
self._set_face(index_keys, face)
|
||||
|
||||
# The tessagon might keep a list of specific face types
|
||||
if 'face_type' in kwargs:
|
||||
if not kwargs['face_type'] in self.tessagon.face_types:
|
||||
self.tessagon.face_types[kwargs['face_type']] = []
|
||||
self.tessagon.face_types[kwargs['face_type']].append(face)
|
||||
|
||||
# On the boundary, make sure equivalent faces are set on neighbor tiles
|
||||
self._set_equivalent_neighbor_faces(index_keys, face, **kwargs)
|
||||
|
||||
return face
|
||||
|
||||
def num_color_patterns(self):
|
||||
return self.tessagon.num_color_patterns()
|
||||
|
||||
def calculate_colors(self):
|
||||
if self.color_pattern > self.num_color_patterns():
|
||||
raise ValueError("color_pattern must be below %d" %
|
||||
(self.num_color_patterns()))
|
||||
method_name = "color_pattern%d" % (self.color_pattern)
|
||||
method = getattr(self, method_name)
|
||||
if not callable(method):
|
||||
raise ValueError("%s is not a callable color pattern" %
|
||||
(method_name))
|
||||
method()
|
||||
|
||||
def color_face(self, index_keys, color_index):
|
||||
face = self._get_face(index_keys)
|
||||
if face is None:
|
||||
return
|
||||
self.mesh_adaptor.color_face(face, color_index)
|
||||
|
||||
def set_equivalent_face(self, neighbor_keys, index_keys, face, **kwargs):
|
||||
# On boundary, the face on a neighbor is equivalent to this face
|
||||
# This is usually only called indirectly via add_face, but check out
|
||||
# PythagoreanTile for an example of direct usage
|
||||
tile = self.get_neighbor_tile(neighbor_keys)
|
||||
if tile is None:
|
||||
return None
|
||||
tile._set_face(self._index_path(index_keys, neighbor_keys), face)
|
||||
|
||||
def all_face_paths(self, faces=None, base_path=None):
|
||||
if faces is None:
|
||||
faces = self.faces
|
||||
if base_path is None:
|
||||
base_path = []
|
||||
|
||||
paths = []
|
||||
for index in faces:
|
||||
new_base_path = base_path + [index]
|
||||
if type(faces[index]) is dict:
|
||||
paths += self.all_face_paths(faces[index], new_base_path)
|
||||
else:
|
||||
paths.append(new_base_path)
|
||||
return paths
|
||||
|
||||
def color_paths(self, paths, color, color_other=None):
|
||||
for path in self.face_paths:
|
||||
if path in paths:
|
||||
self.color_face(path, color)
|
||||
elif color_other:
|
||||
self.color_face(path, color_other)
|
||||
|
||||
def color_paths_hash(self, hash, color_other=None):
|
||||
for path in self.face_paths:
|
||||
for color in hash:
|
||||
done = False
|
||||
if path in hash[color]:
|
||||
self.color_face(path, color)
|
||||
done = True
|
||||
break
|
||||
if color_other and not done:
|
||||
self.color_face(path, color_other)
|
||||
|
||||
# Below are protected
|
||||
|
||||
def _get_vert(self, index_keys):
|
||||
return self._get_nested_list_value(self.verts, index_keys)
|
||||
|
||||
def _set_vert(self, index_keys, value):
|
||||
self._set_nested_list_value(self.verts, index_keys, value)
|
||||
|
||||
def _get_face(self, index_keys):
|
||||
return self._get_nested_list_value(self.faces, index_keys)
|
||||
|
||||
def _set_face(self, index_keys, value):
|
||||
self._set_nested_list_value(self.faces, index_keys, value)
|
||||
|
||||
def _get_neighbor_vert(self, neighbor_keys, index_keys):
|
||||
# See comment about neighbors in AbstractTile
|
||||
tile = self.get_neighbor_tile(neighbor_keys)
|
||||
if tile is None:
|
||||
return None
|
||||
return tile._get_vert(self._index_path(index_keys, neighbor_keys))
|
||||
|
||||
def _create_symmetric_verts(self, index_keys, ratio_u, ratio_v, **kwargs):
|
||||
# The 'symmetry' keyword is just to ensure we don't recurse forever
|
||||
if 'symmetry' not in kwargs:
|
||||
extra_args = {'symmetry': True}
|
||||
if self.rot_symmetric == 180:
|
||||
rot_keys = self._rotate_index(index_keys)
|
||||
self.add_vert(rot_keys, 1.0 - ratio_u, 1 - ratio_v,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
elif self.rot_symmetric == 90:
|
||||
rot_keys = self._rotate_index(index_keys)
|
||||
self.add_vert(rot_keys, 1.0 - ratio_v, ratio_u,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
rot_keys = self._rotate_index(rot_keys)
|
||||
self.add_vert(rot_keys, 1.0 - ratio_u, 1 - ratio_v,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
rot_keys = self._rotate_index(rot_keys)
|
||||
self.add_vert(rot_keys, ratio_v, 1 - ratio_u,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
if self.u_symmetric:
|
||||
# Add reflection about u
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
self.add_vert(u_flip_keys, 1.0 - ratio_u, ratio_v,
|
||||
**{**kwargs, **extra_args})
|
||||
if self.v_symmetric:
|
||||
# Add diagonally across
|
||||
uv_flip_keys = self._v_flip(u_flip_keys)
|
||||
self.add_vert(uv_flip_keys, 1.0 - ratio_u, 1.0 - ratio_v,
|
||||
**{**kwargs, **extra_args})
|
||||
if self.v_symmetric:
|
||||
# Add reflection about v
|
||||
v_flip_keys = self._v_flip(index_keys)
|
||||
self.add_vert(v_flip_keys, ratio_u, 1.0 - ratio_v,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
def _set_equivalent_neighbor_verts(self, index_keys, vert, **kwargs):
|
||||
if 'u_boundary' in kwargs:
|
||||
self._set_u_equivalent_vert(index_keys, vert, **kwargs)
|
||||
if 'v_boundary' in kwargs:
|
||||
self._set_v_equivalent_vert(index_keys, vert, **kwargs)
|
||||
if 'corner' in kwargs:
|
||||
self._set_u_equivalent_vert(index_keys, vert, **kwargs)
|
||||
self._set_v_equivalent_vert(index_keys, vert, **kwargs)
|
||||
self._set_uv_equivalent_vert(index_keys, vert, **kwargs)
|
||||
|
||||
# Handle vert on left/right boundary
|
||||
def _set_u_equivalent_vert(self, index_keys, vert, **kwargs):
|
||||
u_index = self._u_index(index_keys)
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
self.set_equivalent_vert([u_index], u_flip_keys, vert, **kwargs)
|
||||
|
||||
# Handle vert on top/bottom boundary
|
||||
def _set_v_equivalent_vert(self, index_keys, vert, **kwargs):
|
||||
v_index = self._v_index(index_keys)
|
||||
v_flip_keys = self._v_flip(index_keys)
|
||||
self.set_equivalent_vert([v_index], v_flip_keys, vert, **kwargs)
|
||||
|
||||
# Handle vert on corner, equivalent to vert on diagonal tile
|
||||
def _set_uv_equivalent_vert(self, index_keys, vert, **kwargs):
|
||||
u_index = self._u_index(index_keys)
|
||||
v_index = self._v_index(index_keys)
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
uv_flip_keys = self._v_flip(u_flip_keys)
|
||||
self.set_equivalent_vert([u_index, v_index], uv_flip_keys, vert,
|
||||
**kwargs)
|
||||
|
||||
def _get_verts_from_list(self, vert_index_keys_list):
|
||||
verts = []
|
||||
for vert_index_keys in vert_index_keys_list:
|
||||
if isinstance(vert_index_keys, list) \
|
||||
and isinstance(vert_index_keys[0], list):
|
||||
vert = self._get_neighbor_vert(vert_index_keys[0],
|
||||
vert_index_keys[1])
|
||||
else:
|
||||
vert = self._get_vert(vert_index_keys)
|
||||
|
||||
if vert is None:
|
||||
return None
|
||||
verts.append(vert)
|
||||
|
||||
return verts
|
||||
|
||||
def _create_symmetric_faces(self, index_keys, vert_index_keys_list,
|
||||
**kwargs):
|
||||
# The 'symmetry' keyword is just to ensure we don't recurse forever
|
||||
if 'symmetry' not in kwargs:
|
||||
extra_args = {'symmetry': True}
|
||||
if self.rot_symmetric == 180:
|
||||
rot_keys = self._rotate_index(index_keys)
|
||||
rot_vert_index_keys_list \
|
||||
= self._rotate_index(vert_index_keys_list)
|
||||
if 'equivalent' in kwargs:
|
||||
equivalent_faces = kwargs['equivalent']
|
||||
kwargs = kwargs.copy()
|
||||
kwargs['equivalent'] = \
|
||||
[self._rotate_index(equivalent_face)
|
||||
for equivalent_face in equivalent_faces]
|
||||
|
||||
self.add_face(rot_keys, rot_vert_index_keys_list,
|
||||
**{**kwargs, **extra_args})
|
||||
if self.u_symmetric:
|
||||
# Add reflection about u
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
u_flip_vert_index_keys_list \
|
||||
= self._u_flip(vert_index_keys_list)
|
||||
self.add_face(u_flip_keys, u_flip_vert_index_keys_list,
|
||||
**{**kwargs, **extra_args})
|
||||
if self.v_symmetric:
|
||||
# Add diagonally across
|
||||
uv_flip_keys = self._v_flip(u_flip_keys)
|
||||
uv_flip_vert_index_keys_list \
|
||||
= self._v_flip(u_flip_vert_index_keys_list)
|
||||
self.add_face(uv_flip_keys, uv_flip_vert_index_keys_list,
|
||||
**{**kwargs, **extra_args})
|
||||
if self.v_symmetric:
|
||||
# Add reflection about v
|
||||
v_flip_keys = self._v_flip(index_keys)
|
||||
v_flip_vert_index_keys_list \
|
||||
= self._v_flip(vert_index_keys_list)
|
||||
self.add_face(v_flip_keys, v_flip_vert_index_keys_list,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
def _set_equivalent_neighbor_faces(self, index_keys, face, **kwargs):
|
||||
if 'u_boundary' in kwargs:
|
||||
self._set_u_equivalent_face(index_keys, face, **kwargs)
|
||||
if 'v_boundary' in kwargs:
|
||||
self._set_v_equivalent_face(index_keys, face, **kwargs)
|
||||
if 'corner' in kwargs:
|
||||
self._set_u_equivalent_face(index_keys, face, **kwargs)
|
||||
self._set_v_equivalent_face(index_keys, face, **kwargs)
|
||||
self._set_uv_equivalent_face(index_keys, face, **kwargs)
|
||||
|
||||
# Handle face on left/right boundary
|
||||
def _set_u_equivalent_face(self, index_keys, face, **kwargs):
|
||||
u_index = self._u_index(index_keys)
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
self.set_equivalent_face([u_index], u_flip_keys, face, **kwargs)
|
||||
|
||||
# Handle face on top/bottom boundary
|
||||
def _set_v_equivalent_face(self, index_keys, face, **kwargs):
|
||||
v_index = self._v_index(index_keys)
|
||||
v_flip_keys = self._v_flip(index_keys)
|
||||
self.set_equivalent_face([v_index], v_flip_keys, face, **kwargs)
|
||||
|
||||
# Handle face on corner, equivalent to face on diagonal tile
|
||||
def _set_uv_equivalent_face(self, index_keys, face, **kwargs):
|
||||
u_index = self._u_index(index_keys)
|
||||
v_index = self._v_index(index_keys)
|
||||
u_flip_keys = self._u_flip(index_keys)
|
||||
uv_flip_keys = self._v_flip(u_flip_keys)
|
||||
self.set_equivalent_face([u_index, v_index], uv_flip_keys, face,
|
||||
**kwargs)
|
69
add_mesh_extra_objects/tessagon/core/tile_generator.py
Normal file
69
add_mesh_extra_objects/tessagon/core/tile_generator.py
Normal file
@ -0,0 +1,69 @@
|
||||
from tessagon.core.value_blend import ValueBlend
|
||||
|
||||
|
||||
class TileGenerator(ValueBlend):
|
||||
# This is intended to be an abstract class to generate tiles,
|
||||
# but it's quite tied to a grid structure, so it might make
|
||||
# sense to merge with GridTileGenerator
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
self.tessagon = tessagon
|
||||
|
||||
# Corners is list of tuples:
|
||||
# [topleft, topright, bottomleft, bottomright]
|
||||
self.corners = None
|
||||
self._init_corners(**kwargs)
|
||||
|
||||
self.u_num = kwargs.get('u_num')
|
||||
self.v_num = kwargs.get('v_num')
|
||||
if not self.u_num or not self.v_num:
|
||||
raise ValueError("Make sure u_num and v_num intervals are set")
|
||||
|
||||
self.u_cyclic = kwargs.get('u_cyclic', True)
|
||||
self.v_cyclic = kwargs.get('v_cyclic', True)
|
||||
self.v_twist = kwargs.get('v_twist', False)
|
||||
self.u_twist = kwargs.get('u_twist', False)
|
||||
|
||||
# TODO: delete these?
|
||||
self.u_phase = kwargs.get('u_phase', 0.0)
|
||||
self.v_phase = kwargs.get('v_phase', 0.0)
|
||||
self.u_shear = kwargs.get('u_shear', 0.0)
|
||||
self.v_shear = kwargs.get('v_shear', 0.0)
|
||||
|
||||
# Note: id_prefix is not used for calculation, just debugging
|
||||
self.id_prefix = self.tessagon.__class__.__name__
|
||||
if 'id_prefix' in kwargs:
|
||||
self.id_prefix = kwargs['id_prefix']
|
||||
self.fingerprint_offset = kwargs.get('fingerprint_offset') or None
|
||||
|
||||
self.color_pattern = kwargs.get('color_pattern') or None
|
||||
|
||||
def create_tile(self, u, v, corners, **kwargs):
|
||||
extra_args = {'corners': corners,
|
||||
'fingerprint': [u, v]}
|
||||
if self.fingerprint_offset:
|
||||
extra_args['fingerprint'][0] += self.fingerprint_offset[0]
|
||||
extra_args['fingerprint'][1] += self.fingerprint_offset[1]
|
||||
|
||||
if self.id_prefix:
|
||||
extra_args['id'] = "%s[%d][%d]" % (self.id_prefix, u, v)
|
||||
|
||||
if self.color_pattern:
|
||||
extra_args['color_pattern'] = self.color_pattern
|
||||
|
||||
extra_parameters = self.tessagon.extra_parameters
|
||||
if extra_parameters:
|
||||
for parameter in extra_parameters:
|
||||
extra_args[parameter] = extra_parameters[parameter]
|
||||
|
||||
tile_class = self.tessagon.__class__.tile_class
|
||||
return tile_class(self.tessagon,
|
||||
**{**kwargs, **extra_args})
|
||||
|
||||
def create_tiles(self):
|
||||
self.initialize_tiles()
|
||||
self.initialize_neighbors()
|
||||
tiles = self.get_tiles()
|
||||
for tile in tiles:
|
||||
tile.validate()
|
||||
|
||||
return tiles
|
46
add_mesh_extra_objects/tessagon/core/tile_utils.py
Normal file
46
add_mesh_extra_objects/tessagon/core/tile_utils.py
Normal file
@ -0,0 +1,46 @@
|
||||
def right_tile(index_keys):
|
||||
return [['right'], index_keys]
|
||||
|
||||
|
||||
def left_tile(index_keys):
|
||||
return [['left'], index_keys]
|
||||
|
||||
|
||||
def top_tile(index_keys):
|
||||
return [['top'], index_keys]
|
||||
|
||||
|
||||
def bottom_tile(index_keys):
|
||||
return [['bottom'], index_keys]
|
||||
|
||||
|
||||
def left_top_tile(index_keys):
|
||||
return [['left', 'top'], index_keys]
|
||||
|
||||
|
||||
def left_bottom_tile(index_keys):
|
||||
return [['left', 'bottom'], index_keys]
|
||||
|
||||
|
||||
def right_top_tile(index_keys):
|
||||
return [['right', 'top'], index_keys]
|
||||
|
||||
|
||||
def right_bottom_tile(index_keys):
|
||||
return [['right', 'bottom'], index_keys]
|
||||
|
||||
|
||||
def bottom_left_tile(index_keys):
|
||||
return [['bottom', 'left'], index_keys]
|
||||
|
||||
|
||||
def bottom_right_tile(index_keys):
|
||||
return [['bottom', 'right'], index_keys]
|
||||
|
||||
|
||||
def top_left_tile(index_keys):
|
||||
return [['top', 'left'], index_keys]
|
||||
|
||||
|
||||
def top_right_tile(index_keys):
|
||||
return [['top', 'right'], index_keys]
|
35
add_mesh_extra_objects/tessagon/core/value_blend.py
Normal file
35
add_mesh_extra_objects/tessagon/core/value_blend.py
Normal file
@ -0,0 +1,35 @@
|
||||
class ValueBlend:
|
||||
|
||||
def _init_corners(self, **kwargs):
|
||||
# Corners is list of tuples:
|
||||
# [bottomleft, bottomright, topleft, topright]
|
||||
if 'corners' in kwargs:
|
||||
self.corners = kwargs['corners']
|
||||
if len(self.corners) != 4 or \
|
||||
any(len(v) != 2 for v in self.corners):
|
||||
raise ValueError("corner should be a list of four tuples, "
|
||||
"set either option 'corners' "
|
||||
"or options 'u_range' and 'v_range'")
|
||||
elif 'u_range' in kwargs and 'v_range' in kwargs:
|
||||
self.corners = [[kwargs['u_range'][0], kwargs['v_range'][0]],
|
||||
[kwargs['u_range'][1], kwargs['v_range'][0]],
|
||||
[kwargs['u_range'][0], kwargs['v_range'][1]],
|
||||
[kwargs['u_range'][1], kwargs['v_range'][1]]]
|
||||
else:
|
||||
raise ValueError("Must set either option "
|
||||
"'corners' or options 'u_range' and 'v_range'")
|
||||
|
||||
def _blend_tuples(self, tuple1, tuple2, ratio):
|
||||
out = [None, None]
|
||||
for i in range(2):
|
||||
out[i] = (1 - ratio) * tuple1[i] + ratio * tuple2[i]
|
||||
return out
|
||||
|
||||
def blend(self, ratio_u, ratio_v):
|
||||
uv0 = self._blend_tuples(self.corners[0],
|
||||
self.corners[1],
|
||||
ratio_u)
|
||||
uv1 = self._blend_tuples(self.corners[2],
|
||||
self.corners[3],
|
||||
ratio_u)
|
||||
return self._blend_tuples(uv0, uv1, ratio_v)
|
0
add_mesh_extra_objects/tessagon/misc/__init__.py
Normal file
0
add_mesh_extra_objects/tessagon/misc/__init__.py
Normal file
157
add_mesh_extra_objects/tessagon/misc/shapes.py
Normal file
157
add_mesh_extra_objects/tessagon/misc/shapes.py
Normal file
@ -0,0 +1,157 @@
|
||||
from math import sin, cos, sqrt, pi
|
||||
|
||||
|
||||
def plane(u, v):
|
||||
# u_cyclic = False, v_cyclic = False
|
||||
return [u, v, 0]
|
||||
|
||||
|
||||
def other_plane(u, v):
|
||||
# u_cyclic = False, v_cyclic = False
|
||||
return [v, u, 0]
|
||||
|
||||
|
||||
def general_torus(r1, r2, u, v):
|
||||
x = (r1 + r2*cos(v*2*pi))*cos(u*2*pi)
|
||||
y = (r1 + r2*cos(v*2*pi))*sin(u*2*pi)
|
||||
z = r2*sin(v*2*pi)
|
||||
return [x, y, z]
|
||||
|
||||
|
||||
def normalize_value(v):
|
||||
if (v < 0.0):
|
||||
while (v < 0.0):
|
||||
v += 1.0
|
||||
else:
|
||||
while (v > 1.0):
|
||||
v -= 1.0
|
||||
return v
|
||||
|
||||
|
||||
def warp_var(v, factor):
|
||||
# For any factor, maps 0-->0, 1-->1
|
||||
# factor = 0 is identity
|
||||
# factor > 0 for a wee pinch at v = 1/2
|
||||
v = normalize_value(v)
|
||||
h = 2 * (v - 0.5)
|
||||
i = h + factor*h**3
|
||||
return 0.5*(1.0 + i / (1.0 + factor))
|
||||
|
||||
|
||||
def torus(u, v):
|
||||
# u_cyclic = True, v_cyclic = True
|
||||
r1 = 5.0
|
||||
r2 = 1.0
|
||||
return general_torus(r1, r2, u, warp_var(v, 0.2))
|
||||
|
||||
|
||||
def other_torus(u, v):
|
||||
# u_cyclic = True, v_cyclic = True
|
||||
return torus(v, u)
|
||||
|
||||
|
||||
def general_cylinder(r, h, u, v):
|
||||
x = r*cos(u*2*pi)
|
||||
y = r*sin(u*2*pi)
|
||||
z = h*(v - 0.5)
|
||||
return [x, y, z]
|
||||
|
||||
|
||||
def cylinder(u, v):
|
||||
# u_cyclic = True, v_cyclic = False
|
||||
r = 5.0
|
||||
h = 3.5
|
||||
return general_cylinder(r, h, u, v)
|
||||
|
||||
|
||||
def other_cylinder(u, v):
|
||||
# u_cyclic = False, v_cyclic = True
|
||||
return cylinder(v, u)
|
||||
|
||||
|
||||
def general_paraboloid(scale1, scale2, displace, u, v):
|
||||
return [scale1*u, scale1*v, displace + scale2 * (u**2 + v**2)]
|
||||
|
||||
|
||||
def paraboloid(u, v):
|
||||
# u_cyclic = False, v_cyclic = False
|
||||
return general_paraboloid(4, 3, -3, u, v)
|
||||
|
||||
|
||||
def general_one_sheet_hyperboloid(scale1, scale2, u, v):
|
||||
c = scale1 * sqrt(1 + u**2)
|
||||
v1 = 2*pi*v
|
||||
x = c * cos(v1)
|
||||
y = c * sin(v1)
|
||||
z = scale2 * u
|
||||
return [x, y, z]
|
||||
|
||||
|
||||
def one_sheet_hyperboloid(u, v):
|
||||
# u_cyclic = False, v_cyclic = True
|
||||
return general_one_sheet_hyperboloid(3, 2, u, v)
|
||||
|
||||
|
||||
def general_ellipsoid(r1, r2, r3, u, v):
|
||||
# u_cyclic = True, v_cyclic = False
|
||||
u1 = 2*pi*u
|
||||
v1 = pi*normalize_value(warp_var(v + 0.5, 0.8)-0.5)
|
||||
sinv1 = sin(v1)
|
||||
return [r1 * cos(u1) * sinv1, r2 * sin(u1) * sinv1, r3 * cos(v1)]
|
||||
|
||||
|
||||
def sphere(u, v):
|
||||
return general_ellipsoid(4, 4, 4, u, v)
|
||||
|
||||
|
||||
def general_mobius(r, h, u, v):
|
||||
offset = h*(v-0.5)*sin(u*pi)
|
||||
x = (r + offset)*cos(u*2*pi)
|
||||
y = (r + offset)*sin(u*2*pi)
|
||||
z = h*(v-0.5)*cos(u*pi)
|
||||
return [x, y, z]
|
||||
|
||||
|
||||
def mobius(u, v):
|
||||
# u_cyclic = False, v_cyclic = True
|
||||
# u_twist = True, v_twist = False
|
||||
r = 5.0
|
||||
h = 2.0
|
||||
return general_mobius(r, h, v, u)
|
||||
|
||||
|
||||
def other_mobius(u, v):
|
||||
# u_cyclic = True, v_cyclic = False
|
||||
# u_twist = False, v_twist = True
|
||||
return mobius(v, u)
|
||||
|
||||
|
||||
def general_klein(scale, u, v):
|
||||
# Adapted from http://paulbourke.net/geometry/klein/
|
||||
u1 = 2*pi*normalize_value(warp_var(u + 0.5, 0.6)-0.5)
|
||||
v1 = 2*pi*normalize_value(v+0.25)
|
||||
|
||||
c1 = cos(u1)
|
||||
c2 = sin(u1)
|
||||
r = 4.0 - 2.0*c1
|
||||
|
||||
if u1 <= pi:
|
||||
x = 6*c1*(1.0 + c2) + r*c1*cos(v1)
|
||||
y = 16*c2 + r*c2*cos(v1)
|
||||
else:
|
||||
x = 6*c1*(1.0 + c2) + r*cos(v1+pi)
|
||||
y = 16*c2
|
||||
z = r * sin(v1)
|
||||
return [scale*x, scale*y, scale*z]
|
||||
|
||||
|
||||
def klein(u, v):
|
||||
# u_cyclic = True, v_cyclic = True
|
||||
# u_twist = False, v_twist = True
|
||||
return general_klein(0.25, u, v)
|
||||
|
||||
|
||||
def other_klein(u, v):
|
||||
# u_cyclic = True, v_cyclic = True
|
||||
# u_twist = True, v_twist = False
|
||||
return general_klein(0.25, v, u)
|
0
add_mesh_extra_objects/tessagon/types/__init__.py
Normal file
0
add_mesh_extra_objects/tessagon/types/__init__.py
Normal file
175
add_mesh_extra_objects/tessagon/types/big_hex_tri_tessagon.py
Normal file
175
add_mesh_extra_objects/tessagon/types/big_hex_tri_tessagon.py
Normal file
@ -0,0 +1,175 @@
|
||||
from math import atan2, sqrt, sin, cos, pi
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.alternating_tile import AlternatingTile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import right_tile, left_tile, \
|
||||
top_tile, top_left_tile, bottom_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Big Hexagons and Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['hexagons', 'triangles'],
|
||||
sides=[6, 3],
|
||||
uv_ratio=1.0/sqrt(3),
|
||||
extra_parameters={
|
||||
'hexagon_ratio': {
|
||||
'type': 'float',
|
||||
'min': 0.0,
|
||||
# Any higher than 0.70, and verts are
|
||||
# pushed to neighboring tiles
|
||||
'max': 0.70,
|
||||
'default': 0.5,
|
||||
'description':
|
||||
'Control the size of the Hexagons'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class BigHexTriTile(AlternatingTile):
|
||||
# See the SVG for decomposition:
|
||||
# https://raw.githubusercontent.com/cwant/tessagon/master/documentation/code/big_hex_tri.svg
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = False
|
||||
self.v_symmetric = False
|
||||
|
||||
self.hexagon_ratio = kwargs.get('hexagon_ratio', 0.5)
|
||||
|
||||
# in u units
|
||||
self.hex_radius = 4 * self.hexagon_ratio / sqrt(7)
|
||||
# multiplier to get v units ...
|
||||
self.uv_ratio = self.tessagon.metadata.uv_ratio
|
||||
|
||||
# Tilt
|
||||
self.theta_offset = -atan2(1, 3 * sqrt(3)) + pi/6
|
||||
self.hex_theta = [(self.theta_offset + number * pi / 3.0)
|
||||
for number in range(6)]
|
||||
|
||||
def hex_vert_coord(self, center, number):
|
||||
# number in range(6)
|
||||
return [center[0] +
|
||||
self.hex_radius * cos(self.hex_theta[number]),
|
||||
center[1] +
|
||||
self.hex_radius * sin(self.hex_theta[number]) * self.uv_ratio]
|
||||
|
||||
def init_verts(self):
|
||||
if self.tile_type == 0:
|
||||
verts = {0: None,
|
||||
1: None}
|
||||
else:
|
||||
verts = {2: None,
|
||||
3: None,
|
||||
4: None,
|
||||
5: None}
|
||||
|
||||
return verts
|
||||
|
||||
def init_faces(self):
|
||||
if self.tile_type == 0:
|
||||
faces = {'A': None,
|
||||
'B': None,
|
||||
'C': None,
|
||||
'D': None,
|
||||
'E': None,
|
||||
'F': None,
|
||||
'G': None,
|
||||
'H': None}
|
||||
else:
|
||||
faces = {'I': None,
|
||||
'J': None,
|
||||
'K': None,
|
||||
'L': None,
|
||||
'M': None,
|
||||
'N': None,
|
||||
'O': None,
|
||||
'P': None,
|
||||
'Q': None,
|
||||
'R': None}
|
||||
|
||||
return faces
|
||||
|
||||
def calculate_verts(self):
|
||||
if self.tile_type == 0:
|
||||
self.add_vert([0], *self.hex_vert_coord([0, 1], 5))
|
||||
self.add_vert([1], *self.hex_vert_coord([1, 0], 2))
|
||||
else:
|
||||
self.add_vert([2], *self.hex_vert_coord([1, 1], 3))
|
||||
self.add_vert([3], *self.hex_vert_coord([1, 1], 4))
|
||||
self.add_vert([4], *self.hex_vert_coord([0, 0], 1))
|
||||
self.add_vert([5], *self.hex_vert_coord([0, 0], 0))
|
||||
|
||||
def calculate_faces(self):
|
||||
if self.tile_type == 0:
|
||||
self.add_face('A',
|
||||
[0,
|
||||
top_tile(5),
|
||||
top_tile(4),
|
||||
top_left_tile(1),
|
||||
left_tile(2),
|
||||
left_tile(3)],
|
||||
equivalent=[top_tile('T'),
|
||||
top_left_tile('H'),
|
||||
left_tile('I')])
|
||||
|
||||
self.add_face('B',
|
||||
[0,
|
||||
right_tile(2),
|
||||
top_tile(5)],
|
||||
equivalent=[top_tile('S'),
|
||||
right_tile('K')])
|
||||
|
||||
self.add_face('C',
|
||||
[0,
|
||||
right_tile(4),
|
||||
right_tile(2)],
|
||||
equivalent=[right_tile('L')])
|
||||
|
||||
self.add_face('D',
|
||||
[0,
|
||||
1,
|
||||
right_tile(4)],
|
||||
equivalent=[right_tile('M')])
|
||||
|
||||
self.add_face('E',
|
||||
[1,
|
||||
0,
|
||||
left_tile(3)],
|
||||
equivalent=[left_tile('P')])
|
||||
|
||||
self.add_face('F',
|
||||
[1,
|
||||
left_tile(3),
|
||||
left_tile(5)],
|
||||
equivalent=[left_tile('Q')])
|
||||
|
||||
self.add_face('G',
|
||||
[1,
|
||||
left_tile(5),
|
||||
bottom_tile(2)],
|
||||
equivalent=[left_tile('R')])
|
||||
|
||||
else:
|
||||
self.add_face('N',
|
||||
[2,
|
||||
4,
|
||||
3])
|
||||
|
||||
self.add_face('O',
|
||||
[3,
|
||||
4,
|
||||
5])
|
||||
|
||||
def color_pattern1(self):
|
||||
if self.tile_type == 0:
|
||||
self.color_face('A', 2)
|
||||
self.color_face('B', 1)
|
||||
self.color_face('D', 1)
|
||||
self.color_face('F', 1)
|
||||
else:
|
||||
self.color_face('N', 1)
|
||||
|
||||
|
||||
class BigHexTriTessagon(Tessagon):
|
||||
tile_class = BigHexTriTile
|
||||
metadata = metadata
|
76
add_mesh_extra_objects/tessagon/types/brick_tessagon.py
Normal file
76
add_mesh_extra_objects/tessagon/types/brick_tessagon.py
Normal file
@ -0,0 +1,76 @@
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, bottom_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Bricks',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['rectangles'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class BrickTile(Tile):
|
||||
# top
|
||||
#
|
||||
# -o..o- -o..o- r
|
||||
# ^ .|..|. l .|..|. i
|
||||
# | .o--o. e .o--o. g
|
||||
# | .|..|. f .|..|. h
|
||||
# | -o..o- t -o..o- t
|
||||
# V
|
||||
# U ---> bottom
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': None, 'middle': None, 'bottom': None},
|
||||
'right': {'top': None, 'middle': None, 'bottom': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': None, 'right': None, 'top': None, 'bottom': None}
|
||||
|
||||
def calculate_verts(self):
|
||||
# corners: set bottom-left ... symmetry takes care of other 3 corners
|
||||
self.add_vert(['left', 'bottom'], 0.25, 0.0, v_boundary=True)
|
||||
|
||||
# left middle, symmetry also creates right middle
|
||||
self.add_vert(['left', 'middle'], 0.25, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Add left, symmetry gives the right side face
|
||||
self.add_face('left',
|
||||
[['left', 'top'],
|
||||
['left', 'middle'],
|
||||
['left', 'bottom'],
|
||||
# Verts on neighbor tiles:
|
||||
left_tile(['right', 'bottom']),
|
||||
left_tile(['right', 'middle']),
|
||||
left_tile(['right', 'top'])],
|
||||
u_boundary=True)
|
||||
|
||||
# Add bottom, symmetry gives the top face
|
||||
self.add_face('bottom',
|
||||
[['right', 'bottom'],
|
||||
['right', 'middle'],
|
||||
['left', 'middle'],
|
||||
['left', 'bottom'],
|
||||
# Verts on neighbor tiles:
|
||||
bottom_tile(['left', 'middle']),
|
||||
bottom_tile(['right', 'middle'])],
|
||||
v_boundary=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([
|
||||
['left'],
|
||||
['right']
|
||||
], 1, 0)
|
||||
|
||||
|
||||
class BrickTessagon(Tessagon):
|
||||
tile_class = BrickTile
|
||||
metadata = metadata
|
143
add_mesh_extra_objects/tessagon/types/cloverdale_tessagon.py
Normal file
143
add_mesh_extra_objects/tessagon/types/cloverdale_tessagon.py
Normal file
@ -0,0 +1,143 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_left_tile, top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Cloverdale',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['squares', 'pentagons'],
|
||||
sides=[4, 5],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class CloverdaleTile(Tile):
|
||||
# See the SVG for decomposition:
|
||||
# https://raw.githubusercontent.com/cwant/tessagon/master/documentation/code/cloverdale.svg
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'inner': None,
|
||||
'outer': None,
|
||||
'u_border': None,
|
||||
'v_border': None},
|
||||
'middle': {'inner': None,
|
||||
'outer': None},
|
||||
'bottom': {'inner': None,
|
||||
'outer': None,
|
||||
'u_border': None,
|
||||
'v_border': None}},
|
||||
'center': {'top': {'inner': None,
|
||||
'outer': None},
|
||||
'middle': None,
|
||||
'bottom': {'inner': None,
|
||||
'outer': None}},
|
||||
'right': {'top': {'inner': None,
|
||||
'outer': None,
|
||||
'u_border': None,
|
||||
'v_border': None},
|
||||
'middle': {'inner': None,
|
||||
'outer': None},
|
||||
'bottom': {'inner': None,
|
||||
'outer': None,
|
||||
'u_border': None,
|
||||
'v_border': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'square': None,
|
||||
'u_pentagon': None,
|
||||
'v_pentagon': None},
|
||||
'bottom': {'square': None,
|
||||
'u_pentagon': None,
|
||||
'v_pentagon': None},
|
||||
'middle': {'square': None}},
|
||||
'center': {'top': {'square': None},
|
||||
'bottom': {'square': None}},
|
||||
'right': {'top': {'square': None,
|
||||
'u_pentagon': None,
|
||||
'v_pentagon': None},
|
||||
'bottom': {'square': None,
|
||||
'u_pentagon': None,
|
||||
'v_pentagon': None},
|
||||
'middle': {'square': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# a is the side length of square
|
||||
# c is half diagonal of square
|
||||
c = 1.0 / (sqrt(2.0) + 4.0)
|
||||
a = sqrt(2.0) * c
|
||||
|
||||
# left top corner
|
||||
self.add_vert(['left', 'top', 'inner'],
|
||||
a / 2.0 + c, 1.0 - (a / 2.0 + c))
|
||||
self.add_vert(['left', 'top', 'outer'], a / 2.0, 1.0 - a / 2.0)
|
||||
self.add_vert(['left', 'top', 'u_border'],
|
||||
0.0, 1.0 - a / 2.0, u_boundary=True)
|
||||
self.add_vert(['left', 'top', 'v_border'],
|
||||
a / 2.0, 1.0, v_boundary=True)
|
||||
|
||||
self.add_vert(['left', 'middle', 'inner'], a / 2.0, 0.5)
|
||||
self.add_vert(['left', 'middle', 'outer'], 0.0, 0.5, u_boundary=True)
|
||||
|
||||
self.add_vert(['center', 'top', 'inner'], 0.5, 1.0 - a / 2.0)
|
||||
self.add_vert(['center', 'top', 'outer'], 0.5, 1.0, v_boundary=True)
|
||||
self.add_vert(['center', 'middle'], 0.5, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Middle star
|
||||
self.add_face(['left', 'top', 'square'],
|
||||
[['left', 'top', 'v_border'],
|
||||
['left', 'top', 'outer'],
|
||||
['left', 'top', 'u_border'],
|
||||
left_tile(['right', 'top', 'outer']),
|
||||
left_tile(['right', 'top', 'v_border']),
|
||||
top_left_tile(['right', 'bottom', 'outer']),
|
||||
top_tile(['left', 'bottom', 'u_border']),
|
||||
top_tile(['left', 'bottom', 'outer'])],
|
||||
face_type='star',
|
||||
corner=True)
|
||||
|
||||
self.add_face(['left', 'top', 'u_pentagon'],
|
||||
[['left', 'top', 'u_border'],
|
||||
['left', 'top', 'outer'],
|
||||
['left', 'top', 'inner'],
|
||||
['left', 'middle', 'inner'],
|
||||
['left', 'middle', 'outer']])
|
||||
|
||||
self.add_face(['left', 'top', 'v_pentagon'],
|
||||
[['left', 'top', 'v_border'],
|
||||
['center', 'top', 'outer'],
|
||||
['center', 'top', 'inner'],
|
||||
['left', 'top', 'inner'],
|
||||
['left', 'top', 'outer']])
|
||||
|
||||
self.add_face(['center', 'top', 'square'],
|
||||
[['center', 'middle'],
|
||||
['left', 'top', 'inner'],
|
||||
['center', 'top', 'inner'],
|
||||
['right', 'top', 'inner']])
|
||||
|
||||
self.add_face(['left', 'middle', 'square'],
|
||||
[['center', 'middle'],
|
||||
['left', 'bottom', 'inner'],
|
||||
['left', 'middle', 'inner'],
|
||||
['left', 'top', 'inner']])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['left', 'top', 'square'], 1)
|
||||
self.color_face(['left', 'middle', 'square'], 1)
|
||||
self.color_face(['center', 'top', 'square'], 1)
|
||||
self.color_face(['right', 'middle', 'square'], 1)
|
||||
self.color_face(['center', 'bottom', 'square'], 1)
|
||||
self.color_face(['left', 'top', 'u_pentagon'], 0)
|
||||
self.color_face(['left', 'top', 'v_pentagon'], 0)
|
||||
|
||||
|
||||
class CloverdaleTessagon(Tessagon):
|
||||
tile_class = CloverdaleTile
|
||||
metadata = metadata
|
@ -0,0 +1,167 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Hexagons Dissected with Quads',
|
||||
num_color_patterns=2,
|
||||
classification='laves',
|
||||
shapes=['quads'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class DissectedHexQuadTile(Tile):
|
||||
# 19 verts, 14 quad faces (10 internal, 4 on boundary)
|
||||
#
|
||||
# O+++++++++++++++++++O+++++++++++++++++++O
|
||||
# + + +
|
||||
# + + +
|
||||
# + + +
|
||||
# + + +
|
||||
# + + +
|
||||
# + +O+ +
|
||||
# + ++ ++ +
|
||||
# + ++ ++ +
|
||||
# ++O O++
|
||||
# ++ + + ++
|
||||
# ++ ++ ++ ++
|
||||
# O + + O
|
||||
# + + + +
|
||||
# + + + +
|
||||
# + + + +
|
||||
# + + + +
|
||||
# O+++++++++++++++++++O+++++++++++++++++++O
|
||||
# + + + +
|
||||
# + + + +
|
||||
# + + + +
|
||||
# + + + +
|
||||
# + + + +
|
||||
# O + + O
|
||||
# ++ ++ ++ ++
|
||||
# ++ + + ++
|
||||
# ++O O++
|
||||
# + ++ ++ +
|
||||
# + ++ ++ +
|
||||
# + + + +
|
||||
# + O +
|
||||
# + + +
|
||||
# + + +
|
||||
# + + +
|
||||
# + + +
|
||||
# O+++++++++++++++++++O+++++++++++++++++++O
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'corner': None,
|
||||
'interior': None,
|
||||
'u_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'corner': None,
|
||||
'interior': None,
|
||||
'u_boundary': None}},
|
||||
'right': {'top': {'corner': None,
|
||||
'interior': None,
|
||||
'u_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'corner': None,
|
||||
'interior': None,
|
||||
'u_boundary': None}},
|
||||
'center': {'middle': None,
|
||||
'top': {'v_boundary': None,
|
||||
'interior': None},
|
||||
'bottom': {'v_boundary': None,
|
||||
'interior': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None},
|
||||
'bottom': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None}},
|
||||
'right': {'top': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None},
|
||||
'bottom': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None}},
|
||||
'center': {'top': None,
|
||||
'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
self.add_vert(['left', 'top', 'corner'], 0, 1, corner=True)
|
||||
self.add_vert(['left', 'top', 'interior'], 0.25, 0.75)
|
||||
self.add_vert(['left', 'top', 'u_boundary'], 0, 2.0/3.0,
|
||||
u_boundary=True)
|
||||
|
||||
self.add_vert(['left', 'middle'], 0, 0.5, u_boundary=True)
|
||||
|
||||
self.add_vert(['center', 'middle'], 0.5, 0.5)
|
||||
|
||||
self.add_vert(['center', 'top', 'v_boundary'], 0.5, 1.0,
|
||||
v_boundary=True)
|
||||
self.add_vert(['center', 'top', 'interior'], 0.5, 5.0/6.0)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'top', 'v_boundary'],
|
||||
[['left', 'top', 'corner'],
|
||||
['center', 'top', 'v_boundary'],
|
||||
['center', 'top', 'interior'],
|
||||
['left', 'top', 'interior']])
|
||||
|
||||
self.add_face(['left', 'top', 'u_boundary'],
|
||||
[['left', 'top', 'corner'],
|
||||
['left', 'top', 'interior'],
|
||||
['left', 'top', 'u_boundary'],
|
||||
left_tile(['right', 'top', 'interior'])],
|
||||
u_boundary=True)
|
||||
|
||||
self.add_face(['left', 'top', 'middle'],
|
||||
[['left', 'top', 'interior'],
|
||||
['center', 'middle'],
|
||||
['left', 'middle'],
|
||||
['left', 'top', 'u_boundary']])
|
||||
|
||||
self.add_face(['center', 'top'],
|
||||
[['center', 'middle'],
|
||||
['left', 'top', 'interior'],
|
||||
['center', 'top', 'interior'],
|
||||
['right', 'top', 'interior']])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([['left', 'top', 'middle'],
|
||||
['center', 'top'],
|
||||
['right', 'top', 'middle'],
|
||||
['left', 'bottom', 'middle'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'middle']], 1, 0)
|
||||
|
||||
def color_pattern2(self):
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_paths([['left', 'top', 'middle'],
|
||||
['center', 'top'],
|
||||
['right', 'top', 'middle'],
|
||||
['left', 'bottom', 'middle'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'middle']], 1, 0)
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_paths([
|
||||
['right', 'top', 'v_boundary'],
|
||||
['right', 'bottom', 'v_boundary']], 1, 0)
|
||||
elif self.fingerprint[0] % 3 == 2:
|
||||
self.color_paths([
|
||||
['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'u_boundary'],
|
||||
['left', 'bottom', 'v_boundary'],
|
||||
['left', 'bottom', 'u_boundary']], 1, 0)
|
||||
|
||||
|
||||
class DissectedHexQuadTessagon(Tessagon):
|
||||
tile_class = DissectedHexQuadTile
|
||||
metadata = metadata
|
@ -0,0 +1,135 @@
|
||||
from math import sqrt
|
||||
from tessagon.types.dissected_hex_quad_tessagon import DissectedHexQuadTile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
|
||||
metadata = TessagonMetadata(name='Hexagons Dissected with Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='laves',
|
||||
shapes=['triangles'],
|
||||
sides=[3],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
# Uses the same configuration of vertices as DissectedHexQuadTile
|
||||
class DissectedHexTriTile(DissectedHexQuadTile):
|
||||
|
||||
# 19 verts, 24 internal triangle faces
|
||||
#
|
||||
# O+++++++++++++++++++O+++++++++++++++++++O
|
||||
# ++ ++ + ++ ++
|
||||
# + + ++ + ++ + +
|
||||
# + + ++ + ++ + +
|
||||
# + + + + + + +
|
||||
# + + ++ + ++ + +
|
||||
# + + +O+ + +
|
||||
# + + ++ + ++ + +
|
||||
# + + ++ + ++ + +
|
||||
# + ++O + O++ +
|
||||
# + ++ + + + ++ +
|
||||
# +++ ++ + ++ +++
|
||||
# O+ + + + +O
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# + +++++ +
|
||||
# O+++++++++++++++++++O+++++++++++++++++++O
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# + ++ + + + ++ +
|
||||
# ++ + + + ++
|
||||
# O++ ++ + ++ ++O
|
||||
# + ++ + + + ++ +
|
||||
# + ++O + O++ +
|
||||
# + + + + ++ + +
|
||||
# + + ++ + ++ + +
|
||||
# + + +O+ + +
|
||||
# + + ++ + ++ + +
|
||||
# + + + + + + +
|
||||
# + + ++ + ++ + +
|
||||
# + + ++ + ++ + +
|
||||
# ++ ++ + ++ ++
|
||||
# O+++++++++++++++++++++++++++++++++++++++O
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None,
|
||||
'center': None,
|
||||
'interior1': None, # Touches corner
|
||||
'interior2': None},
|
||||
'bottom': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None,
|
||||
'center': None,
|
||||
'interior1': None,
|
||||
'interior2': None}},
|
||||
'right': {'top': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None,
|
||||
'center': None,
|
||||
'interior1': None,
|
||||
'interior2': None},
|
||||
'bottom': {'v_boundary': None,
|
||||
'u_boundary': None,
|
||||
'middle': None,
|
||||
'center': None,
|
||||
'interior1': None,
|
||||
'interior2': None}}}
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'top', 'v_boundary'],
|
||||
[['left', 'top', 'corner'],
|
||||
['center', 'top', 'v_boundary'],
|
||||
['center', 'top', 'interior']])
|
||||
|
||||
self.add_face(['left', 'top', 'interior1'],
|
||||
[['left', 'top', 'corner'],
|
||||
['center', 'top', 'interior'],
|
||||
['left', 'top', 'interior']])
|
||||
|
||||
self.add_face(['left', 'top', 'u_boundary'],
|
||||
[['left', 'top', 'corner'],
|
||||
['left', 'top', 'interior'],
|
||||
['left', 'top', 'u_boundary']])
|
||||
|
||||
self.add_face(['left', 'top', 'middle'],
|
||||
[['center', 'middle'],
|
||||
['left', 'middle'],
|
||||
['left', 'top', 'u_boundary']])
|
||||
|
||||
self.add_face(['left', 'top', 'interior2'],
|
||||
[['left', 'top', 'interior'],
|
||||
['center', 'middle'],
|
||||
['left', 'top', 'u_boundary']])
|
||||
|
||||
self.add_face(['left', 'top', 'center'],
|
||||
[['center', 'middle'],
|
||||
['left', 'top', 'interior'],
|
||||
['center', 'top', 'interior']])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([
|
||||
['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'u_boundary'],
|
||||
['left', 'top', 'middle'],
|
||||
['left', 'top', 'center'],
|
||||
|
||||
['right', 'top', 'interior1'],
|
||||
['right', 'top', 'interior2'],
|
||||
|
||||
['right', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'u_boundary'],
|
||||
['right', 'bottom', 'middle'],
|
||||
['right', 'bottom', 'center'],
|
||||
|
||||
['left', 'bottom', 'interior1'],
|
||||
['left', 'bottom', 'interior2'],
|
||||
], 1, 0)
|
||||
|
||||
|
||||
class DissectedHexTriTessagon(Tessagon):
|
||||
tile_class = DissectedHexTriTile
|
||||
metadata = metadata
|
@ -0,0 +1,112 @@
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
|
||||
metadata = TessagonMetadata(name='Dissected Square',
|
||||
num_color_patterns=2,
|
||||
classification='laves',
|
||||
shapes=['triangles'],
|
||||
sides=[3],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class DissectedSquareTile(Tile):
|
||||
|
||||
# VERTS: a = ['top', 'left']
|
||||
# a---b---c b = ['top', 'center']
|
||||
# ^ |\..|../| c = ['top', 'right']
|
||||
# | |.\.|./.| d = ['middle', 'left']
|
||||
# | d---e---f e = ['middle', 'center']
|
||||
# | |../|\..| f = ['middle', 'right']
|
||||
# |./.|.\.| g = ['bottom', 'left']
|
||||
# V g---h---i h = ['bottom', 'center']
|
||||
# i = ['bottom', 'right']
|
||||
# U --->
|
||||
|
||||
# FACES:
|
||||
# o---o---o A = ['top', 'left', 'middle']
|
||||
# ^ |\.B|C./| B = ['top', 'left', 'center']
|
||||
# | |A\.|./D| C = ['top', 'right', 'center']
|
||||
# | o---o---o D = ['top', 'right', 'middle']
|
||||
# | |E./|\.H| E = ['bottom', 'left', 'middle']
|
||||
# |./F|G\.| F = ['bottom', 'left', 'center']
|
||||
# V o-------o G = ['bottom', 'right', 'center']
|
||||
# H = ['bottom', 'right', 'middle']
|
||||
# U --->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'middle': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'top': {'left': {'middle': None, 'center': None},
|
||||
'right': {'middle': None, 'center': None}},
|
||||
'bottom': {'left': {'middle': None, 'center': None},
|
||||
'right': {'middle': None, 'center': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# Symmetry allow you to get nine verts for the price of four.
|
||||
self.add_vert(['top', 'left'], 0, 1.0, corner=True)
|
||||
self.add_vert(['middle', 'left'], 0, 0.5, u_boundary=True)
|
||||
self.add_vert(['top', 'center'], 0.5, 1.0, v_boundary=True)
|
||||
self.add_vert(['middle', 'center'], 0.5, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Symmetry allows you to create eight faces for the price of two
|
||||
self.add_face(['top', 'left', 'middle'],
|
||||
[['top', 'left'],
|
||||
['middle', 'left'],
|
||||
['middle', 'center']])
|
||||
self.add_face(['top', 'left', 'center'],
|
||||
[['top', 'left'],
|
||||
['middle', 'center'],
|
||||
['top', 'center']])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([['top', 'left', 'center'],
|
||||
['top', 'right', 'middle'],
|
||||
['bottom', 'right', 'center'],
|
||||
['bottom', 'left', 'middle']], 1, 0)
|
||||
|
||||
def color_pattern2(self):
|
||||
if (self.fingerprint[0] // 2 + self.fingerprint[1] // 2) % 2 == 0:
|
||||
self.color_tiles(1, 0)
|
||||
else:
|
||||
self.color_tiles(0, 1)
|
||||
|
||||
def color_tiles(self, color1, color2):
|
||||
if self.fingerprint[0] % 2 == 0:
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_paths([['top', 'left', 'center'],
|
||||
['bottom', 'right', 'middle']],
|
||||
color2, color1)
|
||||
else:
|
||||
self.color_paths([['bottom', 'left', 'center'],
|
||||
['top', 'right', 'middle']],
|
||||
color2, color1)
|
||||
else:
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_paths([['top', 'right', 'center'],
|
||||
['bottom', 'left', 'middle']],
|
||||
color2, color1)
|
||||
else:
|
||||
self.color_paths([['bottom', 'right', 'center'],
|
||||
['top', 'left', 'middle']],
|
||||
color2, color1)
|
||||
|
||||
|
||||
class DissectedSquareTessagon(Tessagon):
|
||||
tile_class = DissectedSquareTile
|
||||
metadata = metadata
|
@ -0,0 +1,130 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Dissected Triangle',
|
||||
num_color_patterns=1,
|
||||
classification='laves',
|
||||
shapes=['triangles'],
|
||||
sides=[3],
|
||||
uv_ratio=sqrt(3.0))
|
||||
|
||||
|
||||
class DissectedTriangleTile(Tile):
|
||||
# 11 verts:
|
||||
#
|
||||
# O++++++++++++O + O++++++++++++O
|
||||
# ++ ++ ++ + ++ ++ ++
|
||||
# + + +++ + + + +++ + +
|
||||
# + + ++ + + + ++ + +
|
||||
# + + ++ + + + ++ + +
|
||||
# + O++++++++++++++O++++++++++++++O +
|
||||
# + + ++ + + + ++ + +
|
||||
# + + ++ + + + ++ + +
|
||||
# + + +++ + + + +++ + +
|
||||
# ++ ++ ++ + ++ ++ ++
|
||||
# O++++++++++++O + O++++++++++++O
|
||||
#
|
||||
# 14 faces (10 internal, 4 on boundary)
|
||||
#
|
||||
# O++++++++++++O + O++++++++++++O
|
||||
# ++ ++ ++ + ++ ++ ++
|
||||
# + + +++ 3 + 4 + 5 + 6 +++ + +
|
||||
# + + 2 ++ + + + ++ 7 + +
|
||||
# + + ++ + + + ++ + +
|
||||
# + 1 O++++++++++++++O++++++++++++++O 8 +
|
||||
# + + ++ + + + ++ + +
|
||||
# + + 9 ++ + + + ++ 14 + +
|
||||
# + + +++ 10 + + + 13 +++ + +
|
||||
# ++ ++ ++ + ++ ++ ++
|
||||
# O++++++++++++O 11 + 12 O++++++++++++O
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'corner': None,
|
||||
'v_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'corner': None,
|
||||
'v_boundary': None}},
|
||||
'right': {'top': {'corner': None,
|
||||
'v_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'corner': None,
|
||||
'v_boundary': None}},
|
||||
'center': None}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'center': None,
|
||||
'interior1': None, # closest to center
|
||||
'interior2': None},
|
||||
'middle': None,
|
||||
'bottom': {'center': None,
|
||||
'interior1': None, # closest to center
|
||||
'interior2': None}},
|
||||
'right': {'top': {'center': None,
|
||||
'interior1': None, # closest to center
|
||||
'interior2': None},
|
||||
'middle': None,
|
||||
'bottom': {'center': None,
|
||||
'interior1': None, # closest to center
|
||||
'interior2': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# Four corners, via symmetry
|
||||
self.add_vert(['left', 'top', 'corner'], 0, 1, corner=True)
|
||||
# The center vert
|
||||
self.add_vert('center', 0.5, 0.5)
|
||||
# Vert on boundary
|
||||
self.add_vert(['left', 'top', 'v_boundary'], 1.0/3.0, 1,
|
||||
v_boundary=True)
|
||||
# Interior vert
|
||||
self.add_vert(['left', 'middle'], 1.0/6.0, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'middle'],
|
||||
[['left', 'middle'],
|
||||
['left', 'top', 'corner'],
|
||||
['left', 'bottom', 'corner']])
|
||||
|
||||
self.add_face(['left', 'top', 'center'],
|
||||
[['center'],
|
||||
['left', 'top', 'v_boundary'],
|
||||
top_tile(['center'])], v_boundary=True)
|
||||
|
||||
self.add_face(['left', 'top', 'interior1'],
|
||||
[['center'],
|
||||
['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'corner']])
|
||||
|
||||
self.add_face(['left', 'top', 'interior2'],
|
||||
[['center'],
|
||||
['left', 'middle'],
|
||||
['left', 'top', 'corner']])
|
||||
|
||||
def color_pattern1(self):
|
||||
if self.fingerprint[1] % 3 == 0:
|
||||
self.color_paths([['left', 'middle'],
|
||||
['right', 'middle']], 1, 0)
|
||||
elif self.fingerprint[1] % 3 == 2:
|
||||
self.color_paths([['left', 'top', 'interior2'],
|
||||
['right', 'top', 'interior2'],
|
||||
['left', 'top', 'interior1'],
|
||||
['right', 'top', 'interior1']], 1, 0)
|
||||
self.color_paths([['left', 'bottom', 'center'],
|
||||
['right', 'bottom', 'center']], 1, 0)
|
||||
else:
|
||||
self.color_paths([['left', 'bottom', 'interior2'],
|
||||
['right', 'bottom', 'interior2'],
|
||||
['left', 'bottom', 'interior1'],
|
||||
['right', 'bottom', 'interior1']], 1, 0)
|
||||
|
||||
|
||||
class DissectedTriangleTessagon(Tessagon):
|
||||
tile_class = DissectedTriangleTile
|
||||
metadata = metadata
|
208
add_mesh_extra_objects/tessagon/types/dodeca_tessagon.py
Normal file
208
add_mesh_extra_objects/tessagon/types/dodeca_tessagon.py
Normal file
@ -0,0 +1,208 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile, left_tile, left_top_tile
|
||||
|
||||
# Will my brain survive this one?
|
||||
|
||||
metadata = TessagonMetadata(name='Dodecagons, Hexagons, and Squares',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['dodecagons', 'hexagons', 'squares'],
|
||||
sides=[12, 6, 4],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class DodecaTile(Tile):
|
||||
# 24 verts, 19 faces (7 internal, 12 on boundary)
|
||||
# The angles make it hard to draw all edges, some excluded
|
||||
|
||||
# .......|.4.|.......
|
||||
# . o---o .
|
||||
# . 12 / \ 12 .
|
||||
# . o 6 o .
|
||||
# --o ..\ /.. o--
|
||||
# . \.4.o---o.4./ .
|
||||
# ^ . 6 o o 6 .
|
||||
# | . / \ .
|
||||
# | --o o--
|
||||
# | .4| 12 |4. Number is verts in face
|
||||
# | --o o--
|
||||
# . \ / .
|
||||
# V . 6 o o 6 .
|
||||
# . /...o---o...\ .
|
||||
# --o.4./ \.4.o--
|
||||
# . o 6 o .
|
||||
# . \ / .
|
||||
# . 12 o---o 12 .
|
||||
# .......|.4.|.......
|
||||
#
|
||||
# U ---->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
# u_square means on the square that is on the U-boundary
|
||||
return {'top': {'left': {'u_square': None,
|
||||
'v_square': None,
|
||||
# Inner square, sort v-distance from middle
|
||||
'sq1': None,
|
||||
'sq2': None,
|
||||
'sq3': None,
|
||||
'sq4': None},
|
||||
'right': {'u_square': None,
|
||||
'v_square': None,
|
||||
'sq1': None,
|
||||
'sq2': None,
|
||||
'sq3': None,
|
||||
'sq4': None}},
|
||||
'bottom': {'left': {'u_square': None,
|
||||
'v_square': None,
|
||||
'sq1': None,
|
||||
'sq2': None,
|
||||
'sq3': None,
|
||||
'sq4': None},
|
||||
'right': {'u_square': None,
|
||||
'v_square': None,
|
||||
'sq1': None,
|
||||
'sq2': None,
|
||||
'sq3': None,
|
||||
'sq4': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'dodec': {'top': {'left': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'right': None},
|
||||
'middle': None},
|
||||
'hex': {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}},
|
||||
'square': {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'middle': {'left': None,
|
||||
'right': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# u_unit is the length of the edges expressed as a
|
||||
# proportion of the tile
|
||||
u_unit = 1.0 / (3.0 + sqrt(3))
|
||||
u_h = 0.5*sqrt(3)*u_unit # height of triangle of side u_unit
|
||||
|
||||
u1 = 0.5*u_unit
|
||||
u2 = 0.5 - u1 - u_h
|
||||
u3 = 0.5 - u_unit
|
||||
u4 = 0.5 - u1
|
||||
|
||||
v_unit = 1.0 / (3.0*(1.0 + sqrt(3)))
|
||||
v_h = 0.5*sqrt(3)*v_unit # height of triangle of side v_unit
|
||||
v1 = 1.0 - 0.5*v_unit
|
||||
v2 = v1 - v_h
|
||||
v3 = 0.5 + 2*v_h + 0.5*v_unit
|
||||
v4 = 0.5 + v_h + v_unit
|
||||
v5 = 0.5 + v_h + 0.5*v_unit
|
||||
v6 = 0.5 + 0.5*v_unit
|
||||
|
||||
# Define top left region, other verts defined through symmetry
|
||||
self.add_vert(['top', 'left', 'v_square'], u4, v1)
|
||||
self.add_vert(['top', 'left', 'u_square'], u1, v6)
|
||||
self.add_vert(['top', 'left', 'sq1'], u2, v5)
|
||||
self.add_vert(['top', 'left', 'sq2'], u4, v4)
|
||||
self.add_vert(['top', 'left', 'sq3'], u1, v3)
|
||||
self.add_vert(['top', 'left', 'sq4'], u3, v2)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Top left Dodecagon
|
||||
self.add_face(['dodec', 'top', 'left'],
|
||||
[['top', 'left', 'v_square'],
|
||||
['top', 'left', 'sq4'],
|
||||
['top', 'left', 'sq3'],
|
||||
left_tile(['top', 'right', 'sq3']),
|
||||
left_tile(['top', 'right', 'sq4']),
|
||||
left_tile(['top', 'right', 'v_square']),
|
||||
left_top_tile(['bottom', 'right', 'v_square']),
|
||||
left_top_tile(['bottom', 'right', 'sq4']),
|
||||
left_top_tile(['bottom', 'right', 'sq3']),
|
||||
top_tile(['bottom', 'left', 'sq3']),
|
||||
top_tile(['bottom', 'left', 'sq4']),
|
||||
top_tile(['bottom', 'left', 'v_square'])],
|
||||
face_type='dodecagon', corner=True)
|
||||
# Middle Dodecagon
|
||||
self.add_face(['dodec', 'middle'],
|
||||
[['top', 'left', 'u_square'],
|
||||
['top', 'left', 'sq1'],
|
||||
['top', 'left', 'sq2'],
|
||||
['top', 'right', 'sq2'],
|
||||
['top', 'right', 'sq1'],
|
||||
['top', 'right', 'u_square'],
|
||||
['bottom', 'right', 'u_square'],
|
||||
['bottom', 'right', 'sq1'],
|
||||
['bottom', 'right', 'sq2'],
|
||||
['bottom', 'left', 'sq2'],
|
||||
['bottom', 'left', 'sq1'],
|
||||
['bottom', 'left', 'u_square']],
|
||||
face_type='dodecagon')
|
||||
# Upper square
|
||||
self.add_face(['square', 'top', 'center'],
|
||||
[['top', 'left', 'v_square'],
|
||||
['top', 'right', 'v_square'],
|
||||
top_tile(['bottom', 'right', 'v_square']),
|
||||
top_tile(['bottom', 'left', 'v_square'])],
|
||||
face_type='square', v_boundary=True)
|
||||
# Left square
|
||||
self.add_face(['square', 'middle', 'left'],
|
||||
[['top', 'left', 'u_square'],
|
||||
['bottom', 'left', 'u_square'],
|
||||
left_tile(['bottom', 'right', 'u_square']),
|
||||
left_tile(['top', 'right', 'u_square'])],
|
||||
face_type='square', u_boundary=True)
|
||||
# Interior square
|
||||
self.add_face(['square', 'top', 'left'],
|
||||
[['top', 'left', 'sq1'],
|
||||
['top', 'left', 'sq2'],
|
||||
['top', 'left', 'sq4'],
|
||||
['top', 'left', 'sq3']],
|
||||
face_type='square')
|
||||
# Top Hex
|
||||
self.add_face(['hex', 'top', 'center'],
|
||||
[['top', 'left', 'sq2'],
|
||||
['top', 'left', 'sq4'],
|
||||
['top', 'left', 'v_square'],
|
||||
['top', 'right', 'v_square'],
|
||||
['top', 'right', 'sq4'],
|
||||
['top', 'right', 'sq2']],
|
||||
face_type='hexagon')
|
||||
# Left Hex
|
||||
self.add_face(['hex', 'top', 'left'],
|
||||
[['top', 'left', 'sq3'],
|
||||
['top', 'left', 'sq1'],
|
||||
['top', 'left', 'u_square'],
|
||||
left_tile(['top', 'right', 'u_square']),
|
||||
left_tile(['top', 'right', 'sq1']),
|
||||
left_tile(['top', 'right', 'sq3'])],
|
||||
face_type='hexagon', u_boundary=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['dodec', 'middle'], 1)
|
||||
self.color_face(['dodec', 'top', 'left'], 1)
|
||||
|
||||
self.color_face(['hex', 'top', 'left'], 2)
|
||||
self.color_face(['hex', 'top', 'center'], 2)
|
||||
self.color_face(['hex', 'bottom', 'left'], 2)
|
||||
self.color_face(['hex', 'bottom', 'center'], 2)
|
||||
|
||||
|
||||
class DodecaTessagon(Tessagon):
|
||||
tile_class = DodecaTile
|
||||
metadata = metadata
|
145
add_mesh_extra_objects/tessagon/types/dodeca_tri_tessagon.py
Normal file
145
add_mesh_extra_objects/tessagon/types/dodeca_tri_tessagon.py
Normal file
@ -0,0 +1,145 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import bottom_tile, left_tile, bottom_left_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Dodecagons and Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['dodecagons', 'triangles'],
|
||||
sides=[12, 3],
|
||||
uv_ratio=sqrt(3.0))
|
||||
|
||||
|
||||
class DodecaTriTile(Tile):
|
||||
# 14 verts, 11 faces (3 internal, 8 on boundary)
|
||||
# The angles make it hard to draw all edges, some excluded
|
||||
|
||||
# . ....|3.o---o.3|......
|
||||
# ^ . o o .
|
||||
# | . 12 / \ 12 .
|
||||
# | . o o .
|
||||
# | .-o3| 12 |3o-. Number is verts in face
|
||||
# | . o o .
|
||||
# . \ / .
|
||||
# V . 12 o o 12 .
|
||||
# . ....|3.o---o.3|......
|
||||
#
|
||||
# U ---->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
# u_square means on the square that is on the U-boundary
|
||||
return {'left': {'top': {'v_boundary': None,
|
||||
'diag': None,
|
||||
'tri': None}, # on the triangle
|
||||
'middle': None,
|
||||
'bottom': {'v_boundary': None,
|
||||
'diag': None,
|
||||
'tri': None}},
|
||||
'right': {'top': {'v_boundary': None,
|
||||
'diag': None,
|
||||
'tri': None}, # on the triangle
|
||||
'middle': None,
|
||||
'bottom': {'v_boundary': None,
|
||||
'diag': None,
|
||||
'tri': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'dodec': {'left': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': None,
|
||||
'bottom': None},
|
||||
'center': None},
|
||||
'tri': {'left': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None},
|
||||
'right': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# u_unit is the length of the edges expressed as a
|
||||
# proportion of the tile
|
||||
u_unit = 1.0 / (3.0 + 2.0 * sqrt(3))
|
||||
u_h = 0.5*sqrt(3)*u_unit # height of triangle of side u_unit
|
||||
|
||||
u1 = 0.5*u_unit
|
||||
u2 = u1 + u_h
|
||||
u3 = u2 + u1
|
||||
u4 = u3 + u_h
|
||||
|
||||
v_unit = 1.0 / (2.0 + sqrt(3))
|
||||
v_h = 0.5*sqrt(3)*v_unit # height of triangle of side v_unit
|
||||
v1 = 0
|
||||
v2 = 0.5 * v_unit
|
||||
v3 = v2 + v_h
|
||||
v4 = 0.5
|
||||
|
||||
# Sweet symmetry makes this easy work
|
||||
self.add_vert(['left', 'middle'], u1, v4) # 2 verts added
|
||||
self.add_vert(['left', 'bottom', 'v_boundary'], u4, v1,
|
||||
v_boundary=True) # 4 verts
|
||||
self.add_vert(['left', 'bottom', 'diag'], u3, v2) # 4 verts
|
||||
self.add_vert(['left', 'bottom', 'tri'], u2, v3) # 4 verts
|
||||
|
||||
def calculate_faces(self):
|
||||
# Top left Dodecagon
|
||||
self.add_face(['dodec', 'left', 'bottom'],
|
||||
[['left', 'middle'],
|
||||
['left', 'bottom', 'tri'],
|
||||
['left', 'bottom', 'diag'],
|
||||
bottom_tile(['left', 'top', 'diag']),
|
||||
bottom_tile(['left', 'top', 'tri']),
|
||||
bottom_tile(['left', 'middle']),
|
||||
bottom_left_tile(['right', 'middle']),
|
||||
bottom_left_tile(['right', 'top', 'tri']),
|
||||
bottom_left_tile(['right', 'top', 'diag']),
|
||||
left_tile(['right', 'bottom', 'diag']),
|
||||
left_tile(['right', 'bottom', 'tri']),
|
||||
left_tile(['right', 'middle'])],
|
||||
face_type='dodecagon', corner=True)
|
||||
|
||||
# Middle Dodecagon
|
||||
self.add_face(['dodec', 'center'],
|
||||
[['left', 'bottom', 'tri'],
|
||||
['left', 'bottom', 'diag'],
|
||||
['left', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'diag'],
|
||||
['right', 'bottom', 'tri'],
|
||||
['right', 'top', 'tri'],
|
||||
['right', 'top', 'diag'],
|
||||
['right', 'top', 'v_boundary'],
|
||||
['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'diag'],
|
||||
['left', 'top', 'tri']],
|
||||
face_type='dodecagon')
|
||||
|
||||
# Left triangle
|
||||
self.add_face(['tri', 'left', 'middle'],
|
||||
[['left', 'top', 'tri'],
|
||||
['left', 'bottom', 'tri'],
|
||||
['left', 'middle']],
|
||||
face_type='triangle')
|
||||
|
||||
# bottom-left triangle
|
||||
self.add_face(['tri', 'left', 'bottom'],
|
||||
[['left', 'bottom', 'diag'],
|
||||
['left', 'bottom', 'v_boundary'],
|
||||
bottom_tile(['left', 'top', 'diag'])],
|
||||
face_type='triangle', v_boundary=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([['dodec', 'left', 'bottom'],
|
||||
['dodec', 'center']], 1, 0)
|
||||
|
||||
|
||||
class DodecaTriTessagon(Tessagon):
|
||||
tile_class = DodecaTriTile
|
||||
metadata = metadata
|
192
add_mesh_extra_objects/tessagon/types/floret_tessagon.py
Normal file
192
add_mesh_extra_objects/tessagon/types/floret_tessagon.py
Normal file
@ -0,0 +1,192 @@
|
||||
from math import atan2, sqrt, sin, cos, pi
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.alternating_tile import AlternatingTile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import right_tile, left_tile, \
|
||||
top_tile, top_left_tile, top_right_tile, \
|
||||
bottom_tile, bottom_right_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Florets',
|
||||
num_color_patterns=3,
|
||||
classification='laves',
|
||||
shapes=['pentagons'],
|
||||
sides=[5],
|
||||
uv_ratio=1.0/sqrt(3))
|
||||
|
||||
|
||||
class FloretTile(AlternatingTile):
|
||||
# See the SVG for decomposition:
|
||||
# https://raw.githubusercontent.com/cwant/tessagon/master/documentation/code/floret.svg
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = False
|
||||
self.v_symmetric = False
|
||||
|
||||
# multiplier to get v units ...
|
||||
self.uv_ratio = self.tessagon.metadata.uv_ratio
|
||||
|
||||
# Tilt
|
||||
theta_offset1 = pi/3 - atan2(sqrt(3), 9)
|
||||
theta_offset2 = pi/6
|
||||
|
||||
# No hexagons in this pattern, but verts lie of hexagons
|
||||
# radius is in u inits
|
||||
self.hexagons = [
|
||||
{'radius': 4 / sqrt(21),
|
||||
'hex_theta': [(theta_offset1 + number * pi / 3.0)
|
||||
for number in range(6)]},
|
||||
# Just guessing ...
|
||||
{'radius': 2 / (3 * self.uv_ratio),
|
||||
'hex_theta': [(theta_offset2 + number * pi / 3.0)
|
||||
for number in range(6)]},
|
||||
]
|
||||
|
||||
def hex_vert_coord(self, hexagon_num, center, number):
|
||||
# hexagon_num in range(2)
|
||||
# number in range(6)
|
||||
hexagon = self.hexagons[hexagon_num]
|
||||
return [center[0] +
|
||||
hexagon['radius'] * cos(hexagon['hex_theta'][number]),
|
||||
center[1] +
|
||||
hexagon['radius'] * sin(hexagon['hex_theta'][number]) *
|
||||
self.uv_ratio]
|
||||
|
||||
def init_verts(self):
|
||||
if self.tile_type == 0:
|
||||
verts = {i: None for i in range(6)}
|
||||
else:
|
||||
verts = {i: None for i in range(6, 14)}
|
||||
|
||||
return verts
|
||||
|
||||
def init_faces(self):
|
||||
if self.tile_type == 0:
|
||||
faces = {c: None for c in ['A', 'B', 'C', 'D']}
|
||||
else:
|
||||
faces = {c: None for c in ['E', 'F', 'G', 'H', 'I', 'J', 'K', 'L']}
|
||||
|
||||
return faces
|
||||
|
||||
def calculate_verts(self):
|
||||
if self.tile_type == 0:
|
||||
self.add_vert([2], *self.hex_vert_coord(0, [0, 0], 0))
|
||||
self.add_vert([3], *self.hex_vert_coord(0, [1, 1], 3))
|
||||
else:
|
||||
self.add_vert([6], 1, 0,
|
||||
equivalent=[right_tile(0),
|
||||
bottom_right_tile(13),
|
||||
bottom_tile(5)])
|
||||
self.add_vert([7], *self.hex_vert_coord(0, [1, 0], 2))
|
||||
self.add_vert([8], *self.hex_vert_coord(1, [0, 1], 4),
|
||||
equivalent=[left_tile(1)])
|
||||
self.add_vert([9], *self.hex_vert_coord(0, [0, 1], 4))
|
||||
self.add_vert([10], *self.hex_vert_coord(0, [1, 0], 1))
|
||||
self.add_vert([11], *self.hex_vert_coord(1, [0, 1], 5),
|
||||
equivalent=[right_tile(4)])
|
||||
self.add_vert([12], *self.hex_vert_coord(0, [0, 1], 5))
|
||||
self.add_vert([13], 0, 1,
|
||||
equivalent=[left_tile(5),
|
||||
top_left_tile(6),
|
||||
top_tile(0)])
|
||||
|
||||
def calculate_faces(self):
|
||||
# All of tile type 0 faces overlap tiles of type 1
|
||||
if self.tile_type == 0:
|
||||
return
|
||||
|
||||
# Tile 'E' is handled as Tile 'K' on another tile
|
||||
# Tile 'F' is handled as Tile 'L' on another tile
|
||||
self.add_face('G', [6,
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
7])
|
||||
|
||||
self.add_face('H', [11,
|
||||
10,
|
||||
6,
|
||||
right_tile(2),
|
||||
right_tile(3)],
|
||||
equivalent=[right_tile('B')])
|
||||
|
||||
self.add_face('I', [8,
|
||||
9,
|
||||
13,
|
||||
left_tile(3),
|
||||
left_tile(2)],
|
||||
equivalent=[left_tile('C')])
|
||||
|
||||
self.add_face('J', [13,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12])
|
||||
|
||||
self.add_face('K', [12,
|
||||
11,
|
||||
right_tile(3),
|
||||
right_tile(5),
|
||||
top_right_tile(7)],
|
||||
equivalent=[right_tile('D'),
|
||||
top_right_tile('E')])
|
||||
|
||||
self.add_face('L', [13,
|
||||
12,
|
||||
top_right_tile(7),
|
||||
top_tile(1),
|
||||
top_tile(2)],
|
||||
equivalent=[top_right_tile('F'),
|
||||
top_tile('A')])
|
||||
|
||||
def floret_fingerprint(self, face):
|
||||
fingerprint = list(self.fingerprint.copy())
|
||||
|
||||
fingerprint[0] = fingerprint[0] // 2 + fingerprint[1] // 2
|
||||
|
||||
if face in ['F']:
|
||||
fingerprint[0] -= 1
|
||||
elif face in ['K']:
|
||||
fingerprint[0] += 1
|
||||
|
||||
if self.fingerprint[0] % 2 == 0:
|
||||
if face in ['A', 'B']:
|
||||
fingerprint[0] -= 1
|
||||
else:
|
||||
if face in ['C', 'D']:
|
||||
fingerprint[0] += 1
|
||||
|
||||
if face in ['A', 'B', 'E', 'F', 'G', 'H']:
|
||||
fingerprint[1] -= 1
|
||||
|
||||
return fingerprint
|
||||
|
||||
def color_pattern1(self):
|
||||
pattern = [0, 0, 1]
|
||||
|
||||
for face in self.faces:
|
||||
fingerprint = self.floret_fingerprint(face)
|
||||
offset = (fingerprint[0] + fingerprint[1]) % 3
|
||||
self.color_face(face, pattern[offset])
|
||||
|
||||
def color_pattern2(self):
|
||||
for face in self.faces:
|
||||
fingerprint = self.floret_fingerprint(face)
|
||||
color = (fingerprint[0] + fingerprint[1]) % 3
|
||||
self.color_face(face, color)
|
||||
|
||||
def color_pattern3(self):
|
||||
# Follow a straight line in the pattern to see this ...
|
||||
pattern = [[2, 0, 2, 2, 0, 2],
|
||||
[2, 1, 2, 0, 0, 0]]
|
||||
|
||||
for face in self.faces:
|
||||
fingerprint = self.floret_fingerprint(face)
|
||||
row = fingerprint[1] % 2
|
||||
column = (fingerprint[0] - 2 * fingerprint[1]) % 6
|
||||
self.color_face(face, pattern[row][column])
|
||||
|
||||
|
||||
class FloretTessagon(Tessagon):
|
||||
tile_class = FloretTile
|
||||
metadata = metadata
|
132
add_mesh_extra_objects/tessagon/types/hex_big_tri_tessagon.py
Normal file
132
add_mesh_extra_objects/tessagon/types/hex_big_tri_tessagon.py
Normal file
@ -0,0 +1,132 @@
|
||||
from math import sqrt, atan2, sin, cos, pi
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.alternating_tile import AlternatingTile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import \
|
||||
left_tile, top_tile, top_left_tile, \
|
||||
right_tile, bottom_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Hexagons and Big Triangles',
|
||||
num_color_patterns=2,
|
||||
classification='non_edge',
|
||||
shapes=['hexagons', 'triangles'],
|
||||
sides=[6, 3],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class HexBigTriTile(AlternatingTile):
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
|
||||
self.u_symmetric = False
|
||||
self.v_symmetric = False
|
||||
|
||||
# Future use to control hexagon size?
|
||||
self.hexagon_ratio = 0.5
|
||||
|
||||
# in u units
|
||||
self.hex_radius = 4 * self.hexagon_ratio / sqrt(7)
|
||||
# multiplier to get v units ...
|
||||
self.uv_ratio = self.tessagon.metadata.uv_ratio
|
||||
|
||||
# Tilt
|
||||
self.theta_offset = -atan2(1, 3 * sqrt(3)) + pi/6
|
||||
self.hex_theta = [(self.theta_offset + number * pi / 3.0)
|
||||
for number in range(6)]
|
||||
|
||||
def hex_vert_coord(self, center, number):
|
||||
# number in range(6)
|
||||
return [center[0] +
|
||||
self.hex_radius * cos(self.hex_theta[number]),
|
||||
center[1] +
|
||||
self.hex_radius * sin(self.hex_theta[number]) * self.uv_ratio]
|
||||
|
||||
def init_verts(self):
|
||||
if self.tile_type == 0:
|
||||
verts = {0: None,
|
||||
1: None}
|
||||
else:
|
||||
verts = {2: None,
|
||||
3: None,
|
||||
4: None,
|
||||
5: None}
|
||||
|
||||
return verts
|
||||
|
||||
def init_faces(self):
|
||||
if self.tile_type == 0:
|
||||
faces = {'A': None,
|
||||
'B': None,
|
||||
'C': None,
|
||||
'D': None}
|
||||
else:
|
||||
faces = {'E': None,
|
||||
'F': None,
|
||||
'G': None,
|
||||
'H': None,
|
||||
'I': None,
|
||||
'J': None}
|
||||
|
||||
return faces
|
||||
|
||||
def calculate_verts(self):
|
||||
if self.tile_type == 0:
|
||||
self.add_vert([0], *self.hex_vert_coord([0, 1], 5))
|
||||
self.add_vert([1], *self.hex_vert_coord([1, 0], 2))
|
||||
else:
|
||||
self.add_vert([2], *self.hex_vert_coord([1, 1], 3))
|
||||
self.add_vert([3], *self.hex_vert_coord([1, 1], 4))
|
||||
self.add_vert([4], *self.hex_vert_coord([0, 0], 1))
|
||||
self.add_vert([5], *self.hex_vert_coord([0, 0], 0))
|
||||
|
||||
def calculate_faces(self):
|
||||
if self.tile_type != 0:
|
||||
return
|
||||
|
||||
# Top Hexagon
|
||||
self.add_face('A',
|
||||
[0,
|
||||
left_tile(3),
|
||||
left_tile(2),
|
||||
top_left_tile(1),
|
||||
top_tile(4),
|
||||
top_tile(5)],
|
||||
equivalent=[left_tile('F'),
|
||||
top_left_tile('D'),
|
||||
top_tile('I')])
|
||||
|
||||
# Left Triangle
|
||||
self.add_face('B',
|
||||
[1,
|
||||
0,
|
||||
left_tile(3),
|
||||
left_tile(4),
|
||||
left_tile(5),
|
||||
bottom_tile(2)],
|
||||
equivalent=[bottom_tile('E'),
|
||||
left_tile('H')])
|
||||
|
||||
# Right Triangle
|
||||
self.add_face('C',
|
||||
[0,
|
||||
1,
|
||||
right_tile(4),
|
||||
right_tile(3),
|
||||
right_tile(2),
|
||||
top_tile(5)],
|
||||
equivalent=[right_tile('G'),
|
||||
top_tile('J')])
|
||||
|
||||
def color_pattern1(self):
|
||||
if self.tile_type == 0:
|
||||
self.color_face('A', 1)
|
||||
|
||||
def color_pattern2(self):
|
||||
if self.tile_type == 0:
|
||||
self.color_face('A', 1)
|
||||
self.color_face('B', 2)
|
||||
|
||||
|
||||
class HexBigTriTessagon(Tessagon):
|
||||
tile_class = HexBigTriTile
|
||||
metadata = metadata
|
169
add_mesh_extra_objects/tessagon/types/hex_square_tri_tessagon.py
Normal file
169
add_mesh_extra_objects/tessagon/types/hex_square_tri_tessagon.py
Normal file
@ -0,0 +1,169 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_tile, left_top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Hexagons, Squares, and Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['hexagons', 'squares', 'triangles'],
|
||||
sides=[6, 4, 3],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class HexSquareTriTile(Tile):
|
||||
# 14 verts, 19 faces (7 internal, 12 on boundary)
|
||||
# The angles make it hard to draw all edges, some excluded
|
||||
#
|
||||
# ...|...|... 6..|.4.|..6
|
||||
# ...o---o... ...o---o...
|
||||
# ^ o ..\./...o o.4.\3/.4.o
|
||||
# | .\...o.../. 3\...o.../3 Numbers are faces with # sides
|
||||
# | --o.....o-- --o.....o--
|
||||
# | ..|.....|.. 4.|..6..|.4
|
||||
# | --o.... o-- --o.... o--
|
||||
# ./.. o...\. 3/.. o...\3
|
||||
# V o.../.\...o o.4./3\.4.o
|
||||
# ...o---o... ...o---o...
|
||||
# ...|...|... 6..|.4.|..6
|
||||
#
|
||||
# U ---->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
# u_square means on the square that is on the U-boundary
|
||||
return {'top': {'left': {'u_boundary': None,
|
||||
'u_square': None,
|
||||
'v_square': None},
|
||||
'right': {'u_boundary': None,
|
||||
'u_square': None,
|
||||
'v_square': None},
|
||||
'center': None},
|
||||
'bottom': {'left': {'u_boundary': None,
|
||||
'u_square': None,
|
||||
'v_square': None},
|
||||
'right': {'u_boundary': None,
|
||||
'u_square': None,
|
||||
'v_square': None},
|
||||
'center': None}}
|
||||
|
||||
def init_faces(self):
|
||||
# Whelp!
|
||||
return {'hex': {'top': {'left': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'right': None},
|
||||
'middle': None},
|
||||
'tri': {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}},
|
||||
'square': {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'middle': {'left': None,
|
||||
'right': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# u_unit is the length of the edges expressed as a
|
||||
# proportion of the tile
|
||||
u_unit = 1.0 / (1.0 + sqrt(3))
|
||||
u0 = 0
|
||||
u1 = 0.5*u_unit
|
||||
u2 = 0.5*(1.0-u_unit)
|
||||
u3 = 0.5
|
||||
|
||||
v_unit = 1.0 / (3.0 + sqrt(3))
|
||||
v0 = 1.0 - 0.5*v_unit
|
||||
v1 = 1.0 - v_unit
|
||||
v2 = 0.5 + v_unit
|
||||
v3 = 0.5 + 0.5*v_unit
|
||||
|
||||
# Define top left square, other verts defined through symmetry
|
||||
self.add_vert(['top', 'left', 'v_square'], u2, v0)
|
||||
self.add_vert(['top', 'center'], u3, v2)
|
||||
self.add_vert(['top', 'left', 'u_square'], u1, v3)
|
||||
self.add_vert(['top', 'left', 'u_boundary'], u0, v1, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Middle hexagon
|
||||
self.add_face(['hex', 'middle'],
|
||||
[['top', 'center'],
|
||||
['top', 'left', 'u_square'],
|
||||
['bottom', 'left', 'u_square'],
|
||||
['bottom', 'center'],
|
||||
['bottom', 'right', 'u_square'],
|
||||
['top', 'right', 'u_square']],
|
||||
face_type='hexagon')
|
||||
|
||||
# Six top-left faces, rest defined via symmetry
|
||||
# Top square
|
||||
self.add_face(['square', 'top', 'center'],
|
||||
[['top', 'left', 'v_square'],
|
||||
['top', 'right', 'v_square'],
|
||||
top_tile(['bottom', 'right', 'v_square']),
|
||||
top_tile(['bottom', 'left', 'v_square'])],
|
||||
face_type='square', v_boundary=True)
|
||||
# Left square
|
||||
self.add_face(['square', 'middle', 'left'],
|
||||
[['top', 'left', 'u_square'],
|
||||
['bottom', 'left', 'u_square'],
|
||||
left_tile(['bottom', 'right', 'u_square']),
|
||||
left_tile(['top', 'right', 'u_square'])],
|
||||
face_type='square', u_boundary=True)
|
||||
# Interior square
|
||||
self.add_face(['square', 'top', 'left'],
|
||||
[['top', 'left', 'v_square'],
|
||||
['top', 'center'],
|
||||
['top', 'left', 'u_square'],
|
||||
['top', 'left', 'u_boundary']],
|
||||
face_type='square')
|
||||
# Upper triangle
|
||||
self.add_face(['tri', 'top', 'center'],
|
||||
[['top', 'center'],
|
||||
['top', 'left', 'v_square'],
|
||||
['top', 'right', 'v_square']],
|
||||
face_type='triangle')
|
||||
# Left triangle
|
||||
self.add_face(['tri', 'top', 'left'],
|
||||
[['top', 'left', 'u_square'],
|
||||
['top', 'left', 'u_boundary'],
|
||||
left_tile(['top', 'right', 'u_square'])],
|
||||
face_type='triangle', u_boundary=True)
|
||||
# Corner hexagon
|
||||
self.add_face(['hex', 'top', 'left'],
|
||||
[['top', 'left', 'v_square'],
|
||||
['top', 'left', 'u_boundary'],
|
||||
left_tile(['top', 'right', 'v_square']),
|
||||
left_top_tile(['bottom', 'right', 'v_square']),
|
||||
top_tile(['bottom', 'left', 'u_boundary']),
|
||||
top_tile(['bottom', 'left', 'v_square'])],
|
||||
face_type='hexagon', corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['hex', 'middle'], 1)
|
||||
self.color_face(['hex', 'top', 'left'], 1)
|
||||
|
||||
# TODO: I'm not sure why I have to explicitely color
|
||||
# 'right' and 'bottom' faces (I thought symmetry would do this?)
|
||||
self.color_face(['square', 'top', 'center'], 2)
|
||||
self.color_face(['square', 'top', 'left'], 2)
|
||||
self.color_face(['square', 'top', 'right'], 2)
|
||||
self.color_face(['square', 'middle', 'left'], 2)
|
||||
self.color_face(['square', 'bottom', 'left'], 2)
|
||||
self.color_face(['square', 'bottom', 'right'], 2)
|
||||
|
||||
|
||||
class HexSquareTriTessagon(Tessagon):
|
||||
tile_class = HexSquareTriTile
|
||||
metadata = metadata
|
129
add_mesh_extra_objects/tessagon/types/hex_tessagon.py
Normal file
129
add_mesh_extra_objects/tessagon/types/hex_tessagon.py
Normal file
@ -0,0 +1,129 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile, top_left_tile, left_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Regular Hexagons',
|
||||
num_color_patterns=2,
|
||||
classification='regular',
|
||||
shapes=['hexagons'],
|
||||
sides=[6],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class HexTile(Tile):
|
||||
# VERTS:
|
||||
# ..|..
|
||||
# ..a.. a = ['top', 'center']
|
||||
# ^ ./.\. b = ['top', 'left']
|
||||
# | b...c c = ['top', 'right']
|
||||
# | |...| d = ['bottom', 'left']
|
||||
# | d...e e = ['bottom', 'right']
|
||||
# | .\./. f = ['bottom', 'center']
|
||||
# ..f..
|
||||
# V ..|..
|
||||
#
|
||||
# U --->
|
||||
|
||||
# FACES:
|
||||
# A.|.B
|
||||
# ..o.. A = ['top', 'left']
|
||||
# ^ ./.\. B = ['top', 'right']
|
||||
# | o...o C = ['middle']
|
||||
# | |.C.| D = ['bottom', 'left']
|
||||
# | o...o E = ['bottom', 'right']
|
||||
# | .\./.
|
||||
# ..o..
|
||||
# V D.|.E
|
||||
#
|
||||
# U --->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'top': {'left': None,
|
||||
'right': None},
|
||||
'middle': None,
|
||||
'bottom': {'left': None,
|
||||
'right': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# Symmetry allow you to get six verts for the price of two.
|
||||
|
||||
# Next line also defines the vert at ['bottom', 'center']
|
||||
self.add_vert(['top', 'center'], 0.5, 5.0/6.0)
|
||||
|
||||
# Next line also defines the verts at: ['bottom', 'left']
|
||||
# ['bottom', 'right']
|
||||
# ['top', 'right']
|
||||
self.add_vert(['top', 'left'], 0, 2.0/3.0, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Symmetry allows you to create five faces for the price of two
|
||||
self.add_face('middle', [['top', 'center'],
|
||||
['top', 'left'],
|
||||
['bottom', 'left'],
|
||||
['bottom', 'center'],
|
||||
['bottom', 'right'],
|
||||
['top', 'right']])
|
||||
|
||||
# The next line also defines the faces at: ['top', 'right']
|
||||
# ['bottom', 'right']
|
||||
# ['bottom', 'left']
|
||||
self.add_face(['top', 'left'],
|
||||
# The first two verts of the face are on this tile
|
||||
[['top', 'left'],
|
||||
['top', 'center'],
|
||||
# The other four verts are on neighboring tiles.
|
||||
# E.g., the next one is the ['bottom', 'center']
|
||||
# vert on the top neighbor tile.
|
||||
top_tile(['bottom', 'center']),
|
||||
top_tile(['bottom', 'left']),
|
||||
top_left_tile(['bottom', 'center']),
|
||||
left_tile(['top', 'center'])],
|
||||
# Defining the face as a 'corner' also associates the
|
||||
# created face as one that is shared with
|
||||
# neighboring tiles.
|
||||
corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_paths([['top', 'left'],
|
||||
['bottom', 'left']], 1, 0)
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_paths([['middle']], 1, 0)
|
||||
else:
|
||||
self.color_paths([['top', 'right'],
|
||||
['bottom', 'right']], 1, 0)
|
||||
|
||||
def color_pattern2(self):
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_paths_hash({1: [['top', 'left'],
|
||||
['bottom', 'left']],
|
||||
2: [['top', 'right'],
|
||||
['bottom', 'right']]}, 0)
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_paths_hash({1: [['middle']],
|
||||
2: [['top', 'left'],
|
||||
['bottom', 'left']]}, 0)
|
||||
else:
|
||||
self.color_paths_hash({2: [['middle']],
|
||||
1: [['top', 'right'],
|
||||
['bottom', 'right']]}, 0)
|
||||
|
||||
|
||||
class HexTessagon(Tessagon):
|
||||
tile_class = HexTile
|
||||
metadata = metadata
|
101
add_mesh_extra_objects/tessagon/types/hex_tri_tessagon.py
Normal file
101
add_mesh_extra_objects/tessagon/types/hex_tri_tessagon.py
Normal file
@ -0,0 +1,101 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, left_top_tile, top_tile
|
||||
metadata = TessagonMetadata(name='Hexagons and Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['hexagons', 'triangles'],
|
||||
sides=[6, 3],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class HexTriTile(Tile):
|
||||
# ....o....
|
||||
# .../.\...
|
||||
# ^ --o---o--
|
||||
# | ./.....\.
|
||||
# | o.......o
|
||||
# | .\...../.
|
||||
# | --o---o--
|
||||
# ...\./...
|
||||
# V ....o ...
|
||||
#
|
||||
# U ------>
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'top': None,
|
||||
'left': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None},
|
||||
'right': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None},
|
||||
'bottom': None}
|
||||
|
||||
def init_faces(self):
|
||||
return {'center': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None},
|
||||
'left': {'top': {'triangle': None, 'hexagon': None},
|
||||
'bottom': {'triangle': None, 'hexagon': None}},
|
||||
'right': {'top': {'triangle': None, 'hexagon': None},
|
||||
'bottom': {'triangle': None, 'hexagon': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# top left verts
|
||||
self.add_vert('top', 0.5, 1, v_boundary=True)
|
||||
self.add_vert(['left', 'top'], 0.25, 0.75)
|
||||
self.add_vert(['left', 'middle'], 0, 0.5, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Middle hexagon
|
||||
self.add_face(['center', 'middle'],
|
||||
[['left', 'top'],
|
||||
['left', 'middle'],
|
||||
['left', 'bottom'],
|
||||
['right', 'bottom'],
|
||||
['right', 'middle'],
|
||||
['right', 'top']],
|
||||
face_type='hexagon')
|
||||
# Interior top triangle
|
||||
self.add_face(['center', 'top'],
|
||||
[['top'],
|
||||
['left', 'top'],
|
||||
['right', 'top']],
|
||||
face_type='triangle')
|
||||
|
||||
# Exterior left triangle
|
||||
self.add_face(['left', 'top', 'triangle'],
|
||||
[['left', 'top'],
|
||||
['left', 'middle'],
|
||||
# Verts on neighbor tiles
|
||||
left_tile(['right', 'top'])],
|
||||
face_type='triangle', u_boundary=True)
|
||||
|
||||
# Exterior top-left hexagon
|
||||
self.add_face(['left', 'top', 'hexagon'],
|
||||
[['top'],
|
||||
['left', 'top'],
|
||||
# Verts on neighbor tiles
|
||||
left_tile(['right', 'top']),
|
||||
left_tile('top'),
|
||||
left_top_tile(['right', 'bottom']),
|
||||
top_tile(['left', 'bottom'])],
|
||||
face_type='hexagon', corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
# Color the hexagons
|
||||
self.color_face(['center', 'middle'], 1)
|
||||
self.color_face(['left', 'top', 'hexagon'], 1)
|
||||
|
||||
|
||||
class HexTriTessagon(Tessagon):
|
||||
tile_class = HexTriTile
|
||||
metadata = metadata
|
@ -0,0 +1,127 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_left_tile, top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Islamic Hexagons and Stars',
|
||||
num_color_patterns=1,
|
||||
classification='non_convex',
|
||||
shapes=['hexagons', 'stars'],
|
||||
sides=[6, 12],
|
||||
uv_ratio=sqrt(3.0))
|
||||
|
||||
|
||||
class IslamicHexStarsTile(Tile):
|
||||
# See page 3 of "Islamic Design" by Daud Sutton
|
||||
# See the SVG for decomposition:
|
||||
# https://raw.githubusercontent.com/cwant/tessagon/master/documentation/code/islamic_hex_stars.svg
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'boundary': None,
|
||||
'mid': {'outer': None,
|
||||
'mid': None,
|
||||
'inner': None}},
|
||||
'middle': {'inner': None,
|
||||
'outer': None},
|
||||
'bottom': {'boundary': None,
|
||||
'mid': {'outer': None,
|
||||
'mid': None,
|
||||
'inner': None}}},
|
||||
'center': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': {'boundary': None,
|
||||
'mid': {'outer': None,
|
||||
'mid': None,
|
||||
'inner': None}},
|
||||
'middle': {'inner': None,
|
||||
'outer': None},
|
||||
'bottom': {'boundary': None,
|
||||
'mid': {'outer': None,
|
||||
'mid': None,
|
||||
'inner': None}}},
|
||||
}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'star': None, 'hexagon': None},
|
||||
'middle': {'hexagon': None},
|
||||
'bottom': {'star': None, 'hexagon': None}},
|
||||
'center': {'star': None},
|
||||
'right': {'top': {'star': None, 'hexagon': None},
|
||||
'middle': {'hexagon': None},
|
||||
'bottom': {'star': None, 'hexagon': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# left verts
|
||||
self.add_vert(['left', 'top', 'boundary'], 2/12.0, 1, v_boundary=True)
|
||||
self.add_vert(['left', 'top', 'mid', 'outer'], 1/12.0, 0.75)
|
||||
self.add_vert(['left', 'top', 'mid', 'mid'], 3/12.0, 0.75)
|
||||
self.add_vert(['left', 'top', 'mid', 'inner'], 5/12.0, 0.75)
|
||||
self.add_vert(['left', 'middle', 'outer'], 0, 0.5, u_boundary=True)
|
||||
self.add_vert(['left', 'middle', 'inner'], 4/12, 0.5)
|
||||
|
||||
# center vert
|
||||
self.add_vert(['center', 'top'], 0.5, 1, v_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'top', 'star'],
|
||||
[['left', 'top', 'boundary'],
|
||||
['left', 'top', 'mid', 'mid'],
|
||||
['left', 'top', 'mid', 'outer'],
|
||||
['left', 'middle', 'outer'],
|
||||
left_tile(['right', 'top', 'mid', 'outer']),
|
||||
left_tile(['right', 'top', 'mid', 'mid']),
|
||||
left_tile(['right', 'top', 'boundary']),
|
||||
top_left_tile(['right', 'bottom', 'mid', 'mid']),
|
||||
top_left_tile(['right', 'bottom', 'mid', 'outer']),
|
||||
top_tile(['left', 'middle', 'outer']),
|
||||
top_tile(['left', 'bottom', 'mid', 'outer']),
|
||||
top_tile(['left', 'bottom', 'mid', 'mid'])],
|
||||
face_type='star', corner=True)
|
||||
|
||||
self.add_face(['left', 'top', 'hexagon'],
|
||||
[['center', 'top'],
|
||||
['left', 'top', 'mid', 'inner'],
|
||||
['left', 'top', 'mid', 'mid'],
|
||||
['left', 'top', 'boundary'],
|
||||
top_tile(['left', 'bottom', 'mid', 'mid']),
|
||||
top_tile(['left', 'bottom', 'mid', 'inner'])],
|
||||
face_type='hexagon', v_boundary=True)
|
||||
|
||||
self.add_face(['left', 'middle', 'hexagon'],
|
||||
[['left', 'middle', 'outer'],
|
||||
['left', 'top', 'mid', 'outer'],
|
||||
['left', 'top', 'mid', 'mid'],
|
||||
['left', 'middle', 'inner'],
|
||||
['left', 'bottom', 'mid', 'mid'],
|
||||
['left', 'bottom', 'mid', 'outer']],
|
||||
face_type='hexagon')
|
||||
|
||||
self.add_face(['center', 'star'],
|
||||
[['center', 'top'],
|
||||
['right', 'top', 'mid', 'inner'],
|
||||
['right', 'top', 'mid', 'mid'],
|
||||
['right', 'middle', 'inner'],
|
||||
['right', 'bottom', 'mid', 'mid'],
|
||||
['right', 'bottom', 'mid', 'inner'],
|
||||
['center', 'bottom'],
|
||||
['left', 'bottom', 'mid', 'inner'],
|
||||
['left', 'bottom', 'mid', 'mid'],
|
||||
['left', 'middle', 'inner'],
|
||||
['left', 'top', 'mid', 'mid'],
|
||||
['left', 'top', 'mid', 'inner']],
|
||||
face_type='star')
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['left', 'top', 'star'], 1)
|
||||
self.color_face(['center', 'star'], 1)
|
||||
|
||||
|
||||
class IslamicHexStarsTessagon(Tessagon):
|
||||
tile_class = IslamicHexStarsTile
|
||||
metadata = metadata
|
@ -0,0 +1,121 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_left_tile, top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Islamic Stars and Crosses',
|
||||
num_color_patterns=1,
|
||||
classification='non_convex',
|
||||
shapes=['stars', 'crosses'],
|
||||
sides=[16],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class IslamicStarsCrossesTile(Tile):
|
||||
# See page 9 of "Islamic Design" by Daud Sutton
|
||||
#
|
||||
# ......o......
|
||||
# ...../.\.....
|
||||
# ..o-o...o-o..
|
||||
# ..|.......|..
|
||||
# ..o.......o..
|
||||
# ./.........\.
|
||||
# o...........o
|
||||
# .\........./.
|
||||
# ..o.......o..
|
||||
# ..|.......|..
|
||||
# ..o-o...o-o..
|
||||
# .....\./.....
|
||||
# ......o......
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'v_dominant': None,
|
||||
'point': None,
|
||||
'u_dominant': None},
|
||||
'middle': None,
|
||||
'bottom': {'v_dominant': None,
|
||||
'point': None,
|
||||
'u_dominant': None}},
|
||||
'center': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': {'v_dominant': None,
|
||||
'point': None,
|
||||
'u_dominant': None},
|
||||
'middle': None,
|
||||
'bottom': {'v_dominant': None,
|
||||
'point': None,
|
||||
'u_dominant': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': None,
|
||||
'bottom': None},
|
||||
'center': None,
|
||||
'right': {'top': None,
|
||||
'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
c = 1.0 / (2 * (sqrt(2) + 1))
|
||||
a = c / sqrt(2)
|
||||
|
||||
# left top corner
|
||||
self.add_vert(['left', 'middle'], 0.0, 0.5, u_boundary=True)
|
||||
self.add_vert(['left', 'top', 'u_dominant'], a, 0.5 + a)
|
||||
self.add_vert(['left', 'top', 'point'], a, 1.0 - a)
|
||||
self.add_vert(['left', 'top', 'v_dominant'], 0.5 - a, 1.0 - a)
|
||||
self.add_vert(['center', 'top'], 0.5, 1.0, v_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Middle star
|
||||
self.add_face(['center'],
|
||||
[['left', 'middle'],
|
||||
['left', 'top', 'u_dominant'],
|
||||
['left', 'top', 'point'],
|
||||
['left', 'top', 'v_dominant'],
|
||||
['center', 'top'],
|
||||
['right', 'top', 'v_dominant'],
|
||||
['right', 'top', 'point'],
|
||||
['right', 'top', 'u_dominant'],
|
||||
['right', 'middle'],
|
||||
['right', 'bottom', 'u_dominant'],
|
||||
['right', 'bottom', 'point'],
|
||||
['right', 'bottom', 'v_dominant'],
|
||||
['center', 'bottom'],
|
||||
['left', 'bottom', 'v_dominant'],
|
||||
['left', 'bottom', 'point'],
|
||||
['left', 'bottom', 'u_dominant']],
|
||||
face_type='star')
|
||||
|
||||
# Top left cross
|
||||
self.add_face(['left', 'top'],
|
||||
[['center', 'top'],
|
||||
['left', 'top', 'v_dominant'],
|
||||
['left', 'top', 'point'],
|
||||
['left', 'top', 'u_dominant'],
|
||||
['left', 'middle'],
|
||||
left_tile(['right', 'top', 'u_dominant']),
|
||||
left_tile(['right', 'top', 'point']),
|
||||
left_tile(['right', 'top', 'v_dominant']),
|
||||
left_tile(['center', 'top']),
|
||||
top_left_tile(['right', 'bottom', 'v_dominant']),
|
||||
top_left_tile(['right', 'bottom', 'point']),
|
||||
top_left_tile(['right', 'bottom', 'u_dominant']),
|
||||
top_left_tile(['right', 'middle']),
|
||||
top_tile(['left', 'bottom', 'u_dominant']),
|
||||
top_tile(['left', 'bottom', 'point']),
|
||||
top_tile(['left', 'bottom', 'v_dominant'])],
|
||||
face_type='cross', corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['left', 'top'], 1)
|
||||
self.color_face(['center'], 0)
|
||||
|
||||
|
||||
class IslamicStarsCrossesTessagon(Tessagon):
|
||||
tile_class = IslamicStarsCrossesTile
|
||||
metadata = metadata
|
84
add_mesh_extra_objects/tessagon/types/octo_tessagon.py
Normal file
84
add_mesh_extra_objects/tessagon/types/octo_tessagon.py
Normal file
@ -0,0 +1,84 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_tile
|
||||
|
||||
# TODO: gulp, 'octagon' does not begin with 'octo'
|
||||
|
||||
metadata = TessagonMetadata(name='Octagons and Squares',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['octagons', 'squares'],
|
||||
sides=[8, 4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class OctoTile(Tile):
|
||||
# ^ ..o-o..
|
||||
# | ./...\.
|
||||
# | o.....o
|
||||
# | |.....|
|
||||
# | o.....o
|
||||
# | .\.../.
|
||||
# ..o-o..
|
||||
# V
|
||||
# U ---->
|
||||
|
||||
CORNER_TO_VERT_RATIO = 1.0 / (2.0 + sqrt(2))
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None}},
|
||||
'right': {'top': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'middle': None,
|
||||
'left': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': None,
|
||||
'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
self.add_vert(['left', 'top', 'v_boundary'],
|
||||
self.CORNER_TO_VERT_RATIO, 1, v_boundary=True)
|
||||
self.add_vert(['left', 'top', 'u_boundary'],
|
||||
0, 1.0 - self.CORNER_TO_VERT_RATIO, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Middle interior face
|
||||
self.add_face('middle', [['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'u_boundary'],
|
||||
['left', 'bottom', 'u_boundary'],
|
||||
['left', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'u_boundary'],
|
||||
['right', 'top', 'u_boundary'],
|
||||
['right', 'top', 'v_boundary']])
|
||||
|
||||
# Four faces, define top left corner, others via symmetry
|
||||
self.add_face(['left', 'top'],
|
||||
[['left', 'top', 'v_boundary'],
|
||||
['left', 'top', 'u_boundary'],
|
||||
# Verts on neighbor tiles
|
||||
left_tile(['right', 'top', 'v_boundary']),
|
||||
top_tile(['left', 'bottom', 'u_boundary'])],
|
||||
corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['middle'], 1)
|
||||
|
||||
|
||||
class OctoTessagon(Tessagon):
|
||||
tile_class = OctoTile
|
||||
metadata = metadata
|
97
add_mesh_extra_objects/tessagon/types/penta2_tessagon.py
Normal file
97
add_mesh_extra_objects/tessagon/types/penta2_tessagon.py
Normal file
@ -0,0 +1,97 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Other Pentagons',
|
||||
num_color_patterns=1,
|
||||
classification='laves',
|
||||
shapes=['pentagons'],
|
||||
sides=[5],
|
||||
uv_ratio=1.0/(2.0+sqrt(3.0)))
|
||||
|
||||
|
||||
class Penta2Tile(Tile):
|
||||
# 11 verts, 6 faces (2 internal, 4 on boundary)
|
||||
#
|
||||
# O---O
|
||||
# |...|
|
||||
# O...O
|
||||
# .\./.
|
||||
# ^ ..O..
|
||||
# | ..|..
|
||||
# | --O--
|
||||
# | ..|..
|
||||
# ..O..
|
||||
# V ./.\.
|
||||
# O...O
|
||||
# |...|
|
||||
# O---O
|
||||
#
|
||||
# U ----->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'corner': None,
|
||||
'u_boundary': None},
|
||||
'bottom': {'corner': None,
|
||||
'u_boundary': None}},
|
||||
'right': {'top': {'corner': None,
|
||||
'u_boundary': None},
|
||||
'bottom': {'corner': None,
|
||||
'u_boundary': None}},
|
||||
'center': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': None,
|
||||
'bottom': None},
|
||||
'center': {'top': None,
|
||||
'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
v_unit = 1.0 / (2.0 + sqrt(3.0))
|
||||
v0 = 0
|
||||
v1 = v_unit * 0.5 * (1.0 + 1.0 / sqrt(3.0))
|
||||
v2 = 0.5 - v1
|
||||
|
||||
self.add_vert(['left', 'bottom', 'corner'], 0, v0, corner=True)
|
||||
self.add_vert(['left', 'bottom', 'u_boundary'], 0, v1,
|
||||
u_boundary=True)
|
||||
self.add_vert(['center', 'bottom'], 0.5, v2)
|
||||
self.add_vert(['center', 'middle'], 0.5, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['center', 'bottom'],
|
||||
[['left', 'bottom', 'corner'],
|
||||
['left', 'bottom', 'u_boundary'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'u_boundary'],
|
||||
['right', 'bottom', 'corner']])
|
||||
self.add_face(['left', 'bottom'],
|
||||
[['center', 'middle'],
|
||||
['center', 'bottom'],
|
||||
['left', 'bottom', 'u_boundary'],
|
||||
left_tile(['center', 'bottom']),
|
||||
left_tile(['center', 'middle'])],
|
||||
u_boundary=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([
|
||||
['center', 'top'],
|
||||
['left', 'bottom'],
|
||||
['right', 'bottom']
|
||||
], 1, 0)
|
||||
|
||||
|
||||
class Penta2Tessagon(Tessagon):
|
||||
tile_class = Penta2Tile
|
||||
metadata = metadata
|
137
add_mesh_extra_objects/tessagon/types/penta_tessagon.py
Normal file
137
add_mesh_extra_objects/tessagon/types/penta_tessagon.py
Normal file
@ -0,0 +1,137 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, bottom_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Pentagons',
|
||||
num_color_patterns=1,
|
||||
classification='laves',
|
||||
shapes=['pentagons'],
|
||||
sides=[5],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class PentaTile(Tile):
|
||||
# 16 verts, 12 faces (4 internal, 8 on boundaries)
|
||||
#
|
||||
# + O+++++++++O +
|
||||
# + + + +
|
||||
# O + + O
|
||||
# ++ + + ++
|
||||
# ++ + + ++
|
||||
# O+ +O
|
||||
# ++ ++ ++ ++
|
||||
# + + + +
|
||||
# + O +
|
||||
# + + +
|
||||
# ++++O + O++++
|
||||
# + + +
|
||||
# + O +
|
||||
# + + + +
|
||||
# ++ ++ ++ ++
|
||||
# O+ +O
|
||||
# ++ + + ++
|
||||
# ++ + + ++
|
||||
# O + + O
|
||||
# + + + +
|
||||
# + O+++++++++O +
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'u_boundary': None,
|
||||
'v_boundary': None,
|
||||
'interior': None},
|
||||
'middle': None,
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None,
|
||||
'interior': None}},
|
||||
'center': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': {'u_boundary': None,
|
||||
'v_boundary': None,
|
||||
'interior': None},
|
||||
'middle': None,
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None,
|
||||
'interior': None}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None}},
|
||||
'center': {'top': None,
|
||||
'bottom': None},
|
||||
'right': {'top': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'middle': None,
|
||||
'bottom': {'u_boundary': None,
|
||||
'v_boundary': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# u_unit is the length of the edges expressed as a
|
||||
# proportion of the tile
|
||||
u_unit = 1.0 / (1.0 + sqrt(3))
|
||||
u0 = v0 = 0
|
||||
u1 = v1 = u_unit/(2*sqrt(3))
|
||||
u3 = v3 = (0.5 + 1/sqrt(3)) * u_unit
|
||||
u2 = v2 = 0.5*(u1 + u3)
|
||||
u4 = v4 = 0.5
|
||||
|
||||
self.add_vert(['left', 'bottom', 'u_boundary'], u0, v1,
|
||||
u_boundary=True)
|
||||
self.add_vert(['left', 'bottom', 'v_boundary'], u3, v0,
|
||||
v_boundary=True)
|
||||
self.add_vert(['left', 'bottom', 'interior'], u2, v2)
|
||||
self.add_vert(['left', 'middle'], u1, v4)
|
||||
self.add_vert(['center', 'bottom'], u4, v3)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'bottom', 'u_boundary'],
|
||||
[['left', 'bottom', 'u_boundary'],
|
||||
['left', 'bottom', 'interior'],
|
||||
['left', 'middle'],
|
||||
left_tile(['right', 'middle']),
|
||||
left_tile(['right', 'bottom', 'interior'])],
|
||||
u_boundary=True)
|
||||
|
||||
self.add_face(['left', 'bottom', 'v_boundary'],
|
||||
[['left', 'bottom', 'u_boundary'],
|
||||
['left', 'bottom', 'interior'],
|
||||
['left', 'bottom', 'v_boundary'],
|
||||
bottom_tile(['left', 'top', 'interior']),
|
||||
bottom_tile(['left', 'top', 'u_boundary'])],
|
||||
v_boundary=True)
|
||||
|
||||
self.add_face(['left', 'middle'],
|
||||
[['left', 'middle'],
|
||||
['left', 'bottom', 'interior'],
|
||||
['center', 'bottom'],
|
||||
['center', 'top'],
|
||||
['left', 'top', 'interior']])
|
||||
|
||||
self.add_face(['center', 'bottom'],
|
||||
[['left', 'bottom', 'interior'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'interior'],
|
||||
['right', 'bottom', 'v_boundary'],
|
||||
['left', 'bottom', 'v_boundary']])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([
|
||||
['right', 'middle'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'v_boundary'],
|
||||
['right', 'bottom', 'u_boundary'],
|
||||
], 1, 0)
|
||||
|
||||
|
||||
class PentaTessagon(Tessagon):
|
||||
tile_class = PentaTile
|
||||
metadata = metadata
|
194
add_mesh_extra_objects/tessagon/types/pythagorean_tessagon.py
Normal file
194
add_mesh_extra_objects/tessagon/types/pythagorean_tessagon.py
Normal file
@ -0,0 +1,194 @@
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile, bottom_tile, \
|
||||
left_tile, right_tile, left_bottom_tile, left_top_tile, \
|
||||
right_bottom_tile, right_top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Pythagorean',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['squares'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class PythagoreanTile(Tile):
|
||||
# 29 verts in six rows, six columns
|
||||
# 12 faces (2 sets shared with neighbors)
|
||||
#
|
||||
# o...o-o-o-o row6 o.1.o-o-o-o
|
||||
# |...|.|...| |...|2|...|
|
||||
# o-o-o-o...o row5 o-o-o-o.3.o
|
||||
# ^ |.|...|...| |4|...|...|
|
||||
# | o-o...o-o-o row4 o-o.5.o-o-o
|
||||
# | ..|...|.|.. ..|...|7|..
|
||||
# | ..o-o-o-o.. row3 6.o-o-o-o.8
|
||||
# ..|.|...|.. ..|9|...|..
|
||||
# V o-o-o...o-o row2 o-o-o10.o-o
|
||||
# |...|...|.| |...|...12|
|
||||
# o...o-o-o-o row1 o11.o-o-o-o
|
||||
#
|
||||
# 1 2 3 4 5 6 <-cols
|
||||
#
|
||||
# U ------>
|
||||
|
||||
def init_verts(self):
|
||||
# [col, row], these read like columns
|
||||
return {1: {1: None, 2: None, 4: None, 5: None, 6: None},
|
||||
2: {2: None, 3: None, 4: None, 5: None},
|
||||
3: {1: None, 2: None, 3: None, 5: None, 6: None},
|
||||
4: {1: None, 3: None, 4: None, 5: None, 6: None},
|
||||
5: {1: None, 2: None, 3: None, 4: None, 6: None},
|
||||
6: {1: None, 2: None, 4: None, 5: None, 6: None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
c = {1: 0.0,
|
||||
2: 1/5.0,
|
||||
3: 2/5.0,
|
||||
4: 3/5.0,
|
||||
5: 4/5.0,
|
||||
6: 1.0}
|
||||
for col in self.verts.keys():
|
||||
for row in self.verts[col].keys():
|
||||
# Some verts only get created if neighbors exist
|
||||
if col == 1:
|
||||
if not self.get_neighbor_tile(['left']):
|
||||
if row == 6 and not self.get_neighbor_tile(['top']):
|
||||
continue
|
||||
if not self.get_neighbor_tile(['bottom']):
|
||||
if row == 1 or row == 2:
|
||||
continue
|
||||
|
||||
vert = self.add_vert([col, row], c[col], c[row])
|
||||
if col == 1:
|
||||
self.set_equivalent_vert(*left_tile([6, row]), vert)
|
||||
if row == 6:
|
||||
self.set_equivalent_vert(*left_top_tile([6, 1]), vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*left_bottom_tile([6, 6]),
|
||||
vert)
|
||||
elif col == 6:
|
||||
self.set_equivalent_vert(*right_tile([1, row]), vert)
|
||||
if row == 6:
|
||||
self.set_equivalent_vert(*right_top_tile([1, 1]),
|
||||
vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*right_bottom_tile([1, 6]),
|
||||
vert)
|
||||
if row == 6:
|
||||
self.set_equivalent_vert(*top_tile([col, 1]), vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*bottom_tile([col, 6]), vert)
|
||||
|
||||
def init_faces(self):
|
||||
return {1: None, 2: None, 3: None, 4: None, 5: None, 6: None,
|
||||
7: None, 8: None, 9: None, 10: None, 11: None, 12: None}
|
||||
|
||||
def calculate_faces(self):
|
||||
face = self.add_face(1, [[1, 6],
|
||||
[1, 5],
|
||||
[2, 5],
|
||||
[3, 5],
|
||||
[3, 6],
|
||||
top_tile([3, 2]),
|
||||
top_tile([2, 2]),
|
||||
top_tile([1, 2])])
|
||||
self.set_equivalent_face(*top_tile(11), face)
|
||||
|
||||
self.add_face(2, [[3, 6],
|
||||
[4, 6],
|
||||
[4, 5],
|
||||
[3, 5]])
|
||||
|
||||
self.add_face(3, [[4, 6],
|
||||
[5, 6],
|
||||
[6, 6],
|
||||
[6, 5],
|
||||
[6, 4],
|
||||
[5, 4],
|
||||
[4, 4],
|
||||
[4, 5]])
|
||||
|
||||
self.add_face(4, [[1, 5],
|
||||
[2, 5],
|
||||
[2, 4],
|
||||
[1, 4]])
|
||||
|
||||
self.add_face(5, [[2, 5],
|
||||
[3, 5],
|
||||
[4, 5],
|
||||
[4, 4],
|
||||
[4, 3],
|
||||
[3, 3],
|
||||
[2, 3],
|
||||
[2, 4]])
|
||||
|
||||
face = self.add_face(6, [[1, 4],
|
||||
[2, 4],
|
||||
[2, 3],
|
||||
[2, 2],
|
||||
[1, 2],
|
||||
left_tile([5, 2]),
|
||||
left_tile([5, 3]),
|
||||
left_tile([5, 4])])
|
||||
self.set_equivalent_face(*left_tile(8), face)
|
||||
|
||||
self.add_face(7, [[4, 4],
|
||||
[5, 4],
|
||||
[5, 3],
|
||||
[4, 3]])
|
||||
|
||||
face = self.add_face(8, [[6, 4],
|
||||
[5, 4],
|
||||
[5, 3],
|
||||
[5, 2],
|
||||
[6, 2],
|
||||
right_tile([2, 2]),
|
||||
right_tile([2, 3]),
|
||||
right_tile([2, 4])])
|
||||
self.set_equivalent_face(*right_tile(6), face)
|
||||
|
||||
self.add_face(9, [[2, 3],
|
||||
[3, 3],
|
||||
[3, 2],
|
||||
[2, 2]])
|
||||
|
||||
self.add_face(10, [[3, 3],
|
||||
[4, 3],
|
||||
[5, 3],
|
||||
[5, 2],
|
||||
[5, 1],
|
||||
[4, 1],
|
||||
[3, 1],
|
||||
[3, 2]])
|
||||
|
||||
face = self.add_face(11, [[1, 1],
|
||||
[1, 2],
|
||||
[2, 2],
|
||||
[3, 2],
|
||||
[3, 1],
|
||||
bottom_tile([3, 5]),
|
||||
bottom_tile([2, 5]),
|
||||
bottom_tile([1, 5])])
|
||||
self.set_equivalent_face(*bottom_tile(1), face)
|
||||
|
||||
self.add_face(12, [[5, 2],
|
||||
[6, 2],
|
||||
[6, 1],
|
||||
[5, 1]])
|
||||
|
||||
def color_pattern1(self):
|
||||
# Color the big ones
|
||||
self.color_face([1], 1)
|
||||
self.color_face([3], 1)
|
||||
self.color_face([5], 1)
|
||||
self.color_face([6], 1)
|
||||
self.color_face([8], 1)
|
||||
self.color_face([10], 1)
|
||||
self.color_face([11], 1)
|
||||
|
||||
|
||||
class PythagoreanTessagon(Tessagon):
|
||||
tile_class = PythagoreanTile
|
||||
metadata = metadata
|
93
add_mesh_extra_objects/tessagon/types/rhombus_tessagon.py
Normal file
93
add_mesh_extra_objects/tessagon/types/rhombus_tessagon.py
Normal file
@ -0,0 +1,93 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Rhombuses',
|
||||
num_color_patterns=2,
|
||||
classification='laves',
|
||||
shapes=['rhombuses'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0/sqrt(3.0))
|
||||
|
||||
|
||||
class RhombusTile(Tile):
|
||||
# ..o..
|
||||
# ./|\.
|
||||
# o.|.o
|
||||
# ^ |.o.|
|
||||
# | |/.\|
|
||||
# | o...o
|
||||
# | |\./|
|
||||
# | |.o.|
|
||||
# | o.|.o
|
||||
# .\|/.
|
||||
# V ..o..
|
||||
#
|
||||
# U --->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': None, 'middle': None, 'bottom': None},
|
||||
'center': {'top': {'boundary': None, 'interior': None},
|
||||
'bottom': {'boundary': None, 'interior': None}},
|
||||
'right': {'top': None, 'middle': None, 'bottom': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'middle': None,
|
||||
'left': {'top': {'interior': None, 'exterior': None},
|
||||
'bottom': {'interior': None, 'exterior': None}},
|
||||
'right': {'top': {'interior': None, 'exterior': None},
|
||||
'bottom': {'interior': None, 'exterior': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# 10 verts, do top left quadrant, others via symmetry
|
||||
self.add_vert(['center', 'top', 'boundary'], 0.5, 1, v_boundary=True)
|
||||
self.add_vert(['left', 'top'], 0, 5.0/6.0, u_boundary=True)
|
||||
self.add_vert(['center', 'top', 'interior'], 0.5, 2.0/3.0)
|
||||
self.add_vert(['left', 'middle'], 0, 1.0/2.0, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
# One middle face
|
||||
self.add_face('middle',
|
||||
[['center', 'top', 'interior'],
|
||||
['left', 'middle'],
|
||||
['center', 'bottom', 'interior'],
|
||||
['right', 'middle']], face_type='horizontal')
|
||||
# Eight others, define only left top, others by symmetry
|
||||
self.add_face(['left', 'top', 'interior'],
|
||||
[['center', 'top', 'boundary'],
|
||||
['left', 'top'],
|
||||
['left', 'middle'],
|
||||
['center', 'top', 'interior']], face_type='upward')
|
||||
self.add_face(['left', 'top', 'exterior'],
|
||||
[['center', 'top', 'boundary'],
|
||||
['left', 'top'],
|
||||
# Verts on neighbor tile
|
||||
left_tile(['center', 'top', 'boundary']),
|
||||
top_tile(['left', 'bottom'])],
|
||||
face_type='horizontal', corner=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['middle'], 1)
|
||||
self.color_face(['left', 'top', 'exterior'], 1)
|
||||
|
||||
self.color_face(['left', 'top', 'interior'], 2)
|
||||
self.color_face(['right', 'bottom', 'interior'], 2)
|
||||
|
||||
def color_pattern2(self):
|
||||
self.color_face(['left', 'top', 'interior'], 1)
|
||||
self.color_face(['right', 'top', 'interior'], 1)
|
||||
|
||||
self.color_face(['left', 'bottom', 'interior'], 2)
|
||||
self.color_face(['right', 'bottom', 'interior'], 2)
|
||||
|
||||
|
||||
class RhombusTessagon(Tessagon):
|
||||
tile_class = RhombusTile
|
||||
metadata = metadata
|
104
add_mesh_extra_objects/tessagon/types/square_tessagon.py
Normal file
104
add_mesh_extra_objects/tessagon/types/square_tessagon.py
Normal file
@ -0,0 +1,104 @@
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
|
||||
metadata = TessagonMetadata(name='Regular Squares',
|
||||
num_color_patterns=8,
|
||||
classification='regular',
|
||||
shapes=['squares'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class SquareTile(Tile):
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'top': {'left': None, 'right': None},
|
||||
'bottom': {'left': None, 'right': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'middle': None}
|
||||
|
||||
def calculate_verts(self):
|
||||
self.add_vert(['top', 'left'], 0, 1, corner=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face('middle', [['top', 'left'],
|
||||
['top', 'right'],
|
||||
['bottom', 'right'],
|
||||
['bottom', 'left']])
|
||||
|
||||
def color_pattern1(self):
|
||||
if (self.fingerprint[0] + self.fingerprint[1]) % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
self.color_face(['middle'], 1)
|
||||
|
||||
def color_pattern2(self):
|
||||
if (self.fingerprint[0] + self.fingerprint[1]) % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
elif self.fingerprint[0] % 2 == 0:
|
||||
self.color_face(['middle'], 1)
|
||||
else:
|
||||
self.color_face(['middle'], 2)
|
||||
|
||||
def color_pattern3(self):
|
||||
if (self.fingerprint[0] * self.fingerprint[1]) % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
self.color_face(['middle'], 1)
|
||||
|
||||
def color_pattern4(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
if ((self.fingerprint[1] // 2) + self.fingerprint[0]) % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
self.color_face(['middle'], 1)
|
||||
|
||||
def color_pattern5(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
self.color_face(['middle'], 1)
|
||||
|
||||
def color_pattern6(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
if self.fingerprint[0] % 2 == 0:
|
||||
self.color_face(['middle'], 1)
|
||||
else:
|
||||
self.color_face(['middle'], 2)
|
||||
|
||||
def color_pattern7(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
if ((self.fingerprint[1] // 2) + self.fingerprint[0]) % 2 == 0:
|
||||
self.color_face(['middle'], 1)
|
||||
else:
|
||||
self.color_face(['middle'], 2)
|
||||
|
||||
def color_pattern8(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
if self.fingerprint[0] % 2 == 0:
|
||||
self.color_face(['middle'], 0)
|
||||
else:
|
||||
self.color_face(['middle'], 1)
|
||||
else:
|
||||
if self.fingerprint[0] % 2 == 0:
|
||||
self.color_face(['middle'], 2)
|
||||
else:
|
||||
self.color_face(['middle'], 3)
|
||||
|
||||
|
||||
class SquareTessagon(Tessagon):
|
||||
tile_class = SquareTile
|
||||
metadata = metadata
|
@ -0,0 +1,98 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
|
||||
metadata = TessagonMetadata(name='Other Squares and Triangles',
|
||||
num_color_patterns=1,
|
||||
classification='archimedean',
|
||||
shapes=['squares', 'triangles'],
|
||||
sides=[4, 3],
|
||||
uv_ratio=1.0/(2.0+sqrt(3.0)))
|
||||
|
||||
|
||||
class SquareTri2Tile(Tile):
|
||||
# 6 verts, 11 faces (3 internal, 8 on boundary)
|
||||
#
|
||||
# ^ ..|..
|
||||
# | --O--
|
||||
# | ./.\.
|
||||
# | O---O
|
||||
# |...|
|
||||
# V O---O
|
||||
# .\./.
|
||||
# --O--
|
||||
# ..|..
|
||||
#
|
||||
# U ----->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': {'u_boundary': None},
|
||||
'bottom': {'u_boundary': None}},
|
||||
'right': {'top': {'u_boundary': None},
|
||||
'bottom': {'u_boundary': None}},
|
||||
'center': {'top': None,
|
||||
'bottom': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': {'corner': None,
|
||||
'u_boundary': None},
|
||||
'bottom': {'corner': None,
|
||||
'u_boundary': None}},
|
||||
'right': {'top': {'corner': None,
|
||||
'u_boundary': None},
|
||||
'bottom': {'corner': None,
|
||||
'u_boundary': None}},
|
||||
'center': {'top': None,
|
||||
'middle': None,
|
||||
'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
v_unit = 1.0 / (2 + sqrt(3))
|
||||
v1 = v_unit * 0.5
|
||||
v2 = 0.5 - v1
|
||||
|
||||
# Other verts defined through symmetry
|
||||
self.add_vert(['center', 'bottom'], 0.5, v1)
|
||||
self.add_vert(['left', 'bottom', 'u_boundary'], 0, v2, u_boundary=True)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(['left', 'bottom', 'corner'],
|
||||
[['center', 'bottom'],
|
||||
[['left'], ['center', 'bottom']],
|
||||
[['left', 'bottom'], ['center', 'top']],
|
||||
[['bottom'], ['center', 'top']]],
|
||||
face_type='square', corner=True)
|
||||
|
||||
self.add_face(['left', 'bottom', 'u_boundary'],
|
||||
[['center', 'bottom'],
|
||||
['left', 'bottom', 'u_boundary'],
|
||||
[['left'], ['center', 'bottom']]],
|
||||
face_type='triangle', u_boundary=True)
|
||||
|
||||
self.add_face(['center', 'bottom'],
|
||||
[['left', 'bottom', 'u_boundary'],
|
||||
['center', 'bottom'],
|
||||
['right', 'bottom', 'u_boundary']],
|
||||
face_type='triangle')
|
||||
|
||||
self.add_face(['center', 'middle'],
|
||||
[['left', 'bottom', 'u_boundary'],
|
||||
['right', 'bottom', 'u_boundary'],
|
||||
['right', 'top', 'u_boundary'],
|
||||
['left', 'top', 'u_boundary']],
|
||||
face_type='square')
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([['left', 'bottom', 'corner'],
|
||||
['center', 'middle']], 1, 0)
|
||||
|
||||
|
||||
class SquareTri2Tessagon(Tessagon):
|
||||
tile_class = SquareTri2Tile
|
||||
metadata = metadata
|
145
add_mesh_extra_objects/tessagon/types/square_tri_tessagon.py
Normal file
145
add_mesh_extra_objects/tessagon/types/square_tri_tessagon.py
Normal file
@ -0,0 +1,145 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
|
||||
metadata = TessagonMetadata(name='Squares and Triangles',
|
||||
num_color_patterns=2,
|
||||
classification='archimedean',
|
||||
shapes=['squares', 'triangles'],
|
||||
sides=[4, 3],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class SquareTriTile(Tile):
|
||||
# 12 verts, 16 faces (8 internal, 8 on boundary)
|
||||
# The angles make it hard to draw all edges, some excluded
|
||||
#
|
||||
#
|
||||
# ^ ..o..|..o.. 3.o.3|3.o.3
|
||||
# | ./...o...\. ./...o...\.
|
||||
# | o.../.\...o o.4./3\.4.o
|
||||
# | ...o---o... .3.o---o.3.
|
||||
# o...\./...o o.4.\3/.4.o
|
||||
# V .\...o.../. .\...o.../.
|
||||
# ..o..|..o.. 3.o.3|3.o.3
|
||||
# U ----->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
# u_square means on the square that is on the U-boundary
|
||||
return {'top': {'left': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'right': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'center': None},
|
||||
'bottom': {'left': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'right': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'center': None},
|
||||
'middle': {'left': None,
|
||||
'right': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'tri': {'top': {'left': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'right': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'center': None},
|
||||
'bottom': {'left': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'right': {'u_boundary': None,
|
||||
'v_boundary': None},
|
||||
'center': None},
|
||||
'middle': {'left': None,
|
||||
'right': None}},
|
||||
'square': {'top': {'left': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'right': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# u_unit is the length of the edges expressed as a
|
||||
# proportion of the tile
|
||||
u_unit = 1.0 / (1.0 + sqrt(3))
|
||||
|
||||
u0 = 0
|
||||
u1 = 0.5*u_unit
|
||||
u2 = 0.5*(1.0-u_unit)
|
||||
u3 = 0.5
|
||||
|
||||
v_unit = 1.0 / (1.0 + sqrt(3))
|
||||
v0 = 0.5
|
||||
v1 = 0.5 * (1.0 + v_unit)
|
||||
v2 = 1.0 - 0.5*v_unit
|
||||
v3 = 1.0
|
||||
|
||||
# Define top left square, other verts defined through symmetry
|
||||
self.add_vert(['top', 'left', 'u_boundary'], u0, v1, u_boundary=True)
|
||||
self.add_vert(['top', 'left', 'v_boundary'], u1, v3, v_boundary=True)
|
||||
self.add_vert(['top', 'center'], u3, v2)
|
||||
self.add_vert(['middle', 'left'], u2, v0)
|
||||
|
||||
def calculate_faces(self):
|
||||
# 4 internal squares (others via symmetry)
|
||||
self.add_face(['square', 'top', 'left'],
|
||||
[['top', 'left', 'u_boundary'],
|
||||
['top', 'left', 'v_boundary'],
|
||||
['top', 'center'],
|
||||
['middle', 'left']],
|
||||
face_type='square')
|
||||
|
||||
# 4 u-boundary triangles
|
||||
self.add_face(['tri', 'top', 'left', 'u_boundary'],
|
||||
[['top', 'left', 'v_boundary'],
|
||||
['top', 'left', 'u_boundary'],
|
||||
[['left'], ['top', 'right', 'v_boundary']]],
|
||||
face_type='triangle', u_boundary=True)
|
||||
|
||||
# 4 v-boundary triangles
|
||||
self.add_face(['tri', 'top', 'left', 'v_boundary'],
|
||||
[['top', 'left', 'v_boundary'],
|
||||
['top', 'center'],
|
||||
[['top'], ['bottom', 'center']]],
|
||||
face_type='triangle', v_boundary=True)
|
||||
|
||||
# 2 internal center triangles
|
||||
self.add_face(['tri', 'top', 'center'],
|
||||
[['top', 'center'],
|
||||
['middle', 'right'],
|
||||
['middle', 'left']],
|
||||
face_type='triangle')
|
||||
|
||||
# 2 internal middle triangles
|
||||
self.add_face(['tri', 'middle', 'left'],
|
||||
[['middle', 'left'],
|
||||
['bottom', 'left', 'u_boundary'],
|
||||
['top', 'left', 'u_boundary']],
|
||||
face_type='triangle')
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['square', 'top', 'left'], 1)
|
||||
self.color_face(['square', 'top', 'right'], 1)
|
||||
self.color_face(['square', 'bottom', 'left'], 1)
|
||||
self.color_face(['square', 'bottom', 'right'], 1)
|
||||
|
||||
def color_pattern2(self):
|
||||
self.color_face(['square', 'top', 'left'], 1)
|
||||
self.color_face(['square', 'top', 'right'], 1)
|
||||
self.color_face(['square', 'bottom', 'left'], 1)
|
||||
self.color_face(['square', 'bottom', 'right'], 1)
|
||||
|
||||
self.color_face(['tri', 'middle', 'left'], 2)
|
||||
self.color_face(['tri', 'middle', 'right'], 2)
|
||||
self.color_face(['tri', 'top', 'left', 'v_boundary'], 2)
|
||||
self.color_face(['tri', 'top', 'right', 'v_boundary'], 2)
|
||||
|
||||
|
||||
class SquareTriTessagon(Tessagon):
|
||||
tile_class = SquareTriTile
|
||||
metadata = metadata
|
142
add_mesh_extra_objects/tessagon/types/stanley_park_tessagon.py
Normal file
142
add_mesh_extra_objects/tessagon/types/stanley_park_tessagon.py
Normal file
@ -0,0 +1,142 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile, bottom_tile, \
|
||||
top_left_tile, left_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Stanley Park',
|
||||
num_color_patterns=2,
|
||||
classification='non_convex',
|
||||
sides=[12],
|
||||
uv_ratio=sqrt(3.0))
|
||||
|
||||
# Non-convex pattern. Might work better for 2D than 3D
|
||||
# Also, the ASCII art below is a bit hard to visualize,
|
||||
# so check out preview images linked from README.md
|
||||
|
||||
|
||||
class StanleyParkTile(Tile):
|
||||
# VERTS:
|
||||
# ....a...b.... a = ['top', 'left']
|
||||
# ^ .../.....\... b = ['top', 'right']
|
||||
# | ..c.......d.. c = ['mid1', 'left']
|
||||
# | ..|.......|.. d = ['mid1', 'right']
|
||||
# | ..e...f...g . e = ['mid2', 'left']
|
||||
# | ./.\./.\./.\. f = ['mid2', 'center']
|
||||
# h...i...j...k g = ['mid2', 'right']
|
||||
# V ....|...|.... h = ['mid3', 'left', 'outer']
|
||||
# ....l...m.... i = ['mid3', 'left', 'inner']
|
||||
# j = ['mid3', 'right', 'inner']
|
||||
# U ---> k = ['mid3', 'right', 'outer']
|
||||
# l = ['bottom', 'left']
|
||||
# m = ['bottom', 'right']
|
||||
|
||||
# FACES:
|
||||
# ....o...o....
|
||||
# ^ .A./.....\.C. A = ['top', 'left']
|
||||
# | ..o...B...o.. B = ['top', 'middle']
|
||||
# | ..|.......|.. C = ['top', 'right']
|
||||
# | ..o...o...o . D = ['bottom', 'left']
|
||||
# | ./.\./.\./.\. E = ['bottom', 'middle']
|
||||
# o...o...o...o F = ['bottom', 'right']
|
||||
# V ..D.|.E.|.F..
|
||||
# ....o...o....
|
||||
#
|
||||
# U --->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = False
|
||||
|
||||
def init_verts(self):
|
||||
return {'top': {'left': None,
|
||||
'right': None},
|
||||
'mid1': {'left': None,
|
||||
'right': None},
|
||||
'mid2': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'mid3': {'left': {'outer': None,
|
||||
'inner': None},
|
||||
'right': {'inner': None,
|
||||
'outer': None}},
|
||||
'bottom': {'left': None,
|
||||
'right': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
vert = self.add_vert(['top', 'left'], 2.0/6.0, 1.0)
|
||||
self.set_equivalent_vert(*top_tile(['bottom', 'left']), vert)
|
||||
# Reflection doesn't handle 'set_equivalent_vert' so ...
|
||||
vert = self.add_vert(['top', 'right'], 4.0/6.0, 1.0)
|
||||
self.set_equivalent_vert(*top_tile(['bottom', 'right']), vert)
|
||||
|
||||
self.add_vert(['mid1', 'left'], 1.0/6.0, 5.0/6.0)
|
||||
self.add_vert(['mid2', 'left'], 1.0/6.0, 3.0/6.0)
|
||||
self.add_vert(['mid2', 'center'], 3.0/6.0, 3.0/6.0)
|
||||
self.add_vert(['mid3', 'left', 'outer'], 0.0, 2.0/6.0,
|
||||
u_boundary=True)
|
||||
self.add_vert(['mid3', 'left', 'inner'], 2.0/6.0, 2.0/6.0)
|
||||
|
||||
vert = self.add_vert(['bottom', 'left'], 2.0/6.0, 0.0)
|
||||
self.set_equivalent_vert(*bottom_tile(['top', 'left']), vert)
|
||||
vert = self.add_vert(['bottom', 'right'], 4.0/6.0, 0.0)
|
||||
self.set_equivalent_vert(*bottom_tile(['top', 'right']), vert)
|
||||
|
||||
def calculate_faces(self):
|
||||
face = self.add_face(['top', 'left'],
|
||||
[['mid3', 'left', 'outer'],
|
||||
['mid2', 'left'],
|
||||
['mid1', 'left'],
|
||||
['top', 'left'],
|
||||
top_tile(['mid3', 'left', 'inner']),
|
||||
top_tile(['mid2', 'left']),
|
||||
top_tile(['mid3', 'left', 'outer']),
|
||||
top_left_tile(['mid2', 'right']),
|
||||
top_left_tile(['mid3', 'right', 'inner']),
|
||||
left_tile(['top', 'right']),
|
||||
left_tile(['mid1', 'right']),
|
||||
left_tile(['mid2', 'right'])],
|
||||
u_boundary=True)
|
||||
self.set_equivalent_face(*top_tile(['bottom', 'left']), face)
|
||||
self.set_equivalent_face(*top_left_tile(['bottom', 'right']), face)
|
||||
self.set_equivalent_face(*left_tile(['top', 'right']), face)
|
||||
|
||||
face = self.add_face(['top', 'center'],
|
||||
[['top', 'left'],
|
||||
['mid1', 'left'],
|
||||
['mid2', 'left'],
|
||||
['mid3', 'left', 'inner'],
|
||||
['mid2', 'center'],
|
||||
['mid3', 'right', 'inner'],
|
||||
['mid2', 'right'],
|
||||
['mid1', 'right'],
|
||||
['top', 'right'],
|
||||
top_tile(['mid3', 'right', 'inner']),
|
||||
top_tile(['mid2', 'center']),
|
||||
top_tile(['mid3', 'left', 'inner'])])
|
||||
self.set_equivalent_face(*top_tile(['bottom', 'center']), face)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['top', 'center'], 1)
|
||||
self.color_face(['bottom', 'center'], 1)
|
||||
|
||||
def color_pattern2(self):
|
||||
if self.fingerprint[1] % 2 == 0:
|
||||
self.color_face(['top', 'left'], 1)
|
||||
self.color_face(['top', 'center'], 1)
|
||||
self.color_face(['top', 'right'], 1)
|
||||
|
||||
|
||||
class StanleyParkTessagon(Tessagon):
|
||||
tile_class = StanleyParkTile
|
||||
metadata = metadata
|
152
add_mesh_extra_objects/tessagon/types/tri_tessagon.py
Normal file
152
add_mesh_extra_objects/tessagon/types/tri_tessagon.py
Normal file
@ -0,0 +1,152 @@
|
||||
from math import sqrt
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Regular Triangles',
|
||||
num_color_patterns=3,
|
||||
classification='regular',
|
||||
shapes=['triangles'],
|
||||
sides=[3],
|
||||
uv_ratio=sqrt(3.0))
|
||||
|
||||
|
||||
class TriTile(Tile):
|
||||
|
||||
# ^ 0.|.1 This is the topology of the tile.
|
||||
# | |\|/| (Not a Dead Kennedy's logo ...).
|
||||
# | |.2.|
|
||||
# | |/|\|
|
||||
# V 3.|.4
|
||||
#
|
||||
# U ----->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
|
||||
def init_verts(self):
|
||||
return {'left': {'top': None, 'bottom': None},
|
||||
'middle': None,
|
||||
'right': {'top': None, 'bottom': None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'left': {'top': None, 'middle': None, 'bottom': None},
|
||||
'right': {'top': None, 'middle': None, 'bottom': None}}
|
||||
|
||||
def calculate_verts(self):
|
||||
# Four corners, via symmetry
|
||||
self.add_vert(['left', 'top'], 0, 1, corner=True)
|
||||
# The middle vert
|
||||
self.add_vert('middle', 0.5, 0.5)
|
||||
|
||||
def calculate_faces(self):
|
||||
# Four corners, via symmetry
|
||||
self.add_face(['left', 'top'],
|
||||
[['left', 'top'],
|
||||
['middle'],
|
||||
# Vert on neighboring tile
|
||||
top_tile(['middle'])], v_boundary=True)
|
||||
# Two interior faces, via symmetry
|
||||
self.add_face(['left', 'middle'],
|
||||
[['left', 'top'],
|
||||
['left', 'bottom'],
|
||||
['middle']])
|
||||
|
||||
def color_pattern1(self):
|
||||
# two colors for triangles pointing in different directions
|
||||
self.color_face(['left', 'top'], 0)
|
||||
self.color_face(['right', 'top'], 1)
|
||||
self.color_face(['left', 'middle'], 1)
|
||||
self.color_face(['right', 'middle'], 0)
|
||||
self.color_face(['left', 'bottom'], 0)
|
||||
self.color_face(['right', 'bottom'], 1)
|
||||
|
||||
def color_pattern2(self):
|
||||
# Two colors, this one is awesome, but complicated
|
||||
if not self.fingerprint:
|
||||
return
|
||||
if self.fingerprint[1] % 3 == 0:
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_0_0()
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_0_1()
|
||||
elif self.fingerprint[1] % 3 == 1:
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_1_0()
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_1_1()
|
||||
else:
|
||||
self.color_1_2()
|
||||
else:
|
||||
if self.fingerprint[0] % 3 == 0:
|
||||
self.color_2_0()
|
||||
elif self.fingerprint[0] % 3 == 1:
|
||||
self.color_2_1()
|
||||
else:
|
||||
self.color_2_2()
|
||||
|
||||
def color_pattern3(self):
|
||||
if not self.fingerprint:
|
||||
return
|
||||
if self.fingerprint[1] % 3 == 2:
|
||||
self.color_paths([['left', 'middle'],
|
||||
['right', 'bottom']], 1, 0)
|
||||
elif self.fingerprint[1] % 3 == 1:
|
||||
self.color_paths([['right', 'top'],
|
||||
['right', 'bottom']], 1, 0)
|
||||
else:
|
||||
self.color_paths([['left', 'middle'],
|
||||
['right', 'top']], 1, 0)
|
||||
|
||||
def color_0_0(self):
|
||||
self.color_paths([], 0, 1)
|
||||
|
||||
def color_0_1(self):
|
||||
paths = [['left', 'top'],
|
||||
['left', 'bottom'],
|
||||
['right', 'middle']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_1_0(self):
|
||||
paths = [['left', 'top'],
|
||||
['left', 'bottom'],
|
||||
['right', 'bottom']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_1_1(self):
|
||||
paths = [['left', 'bottom'],
|
||||
['right', 'top'],
|
||||
['right', 'middle']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_1_2(self):
|
||||
paths = [['left', 'top'],
|
||||
['left', 'middle'],
|
||||
['right', 'middle']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_2_0(self):
|
||||
paths = [['left', 'top'],
|
||||
['left', 'bottom'],
|
||||
['right', 'top']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_2_1(self):
|
||||
paths = [['left', 'top'],
|
||||
['right', 'middle'],
|
||||
['right', 'bottom']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
def color_2_2(self):
|
||||
paths = [['left', 'middle'],
|
||||
['left', 'bottom'],
|
||||
['right', 'middle']]
|
||||
self.color_paths(paths, 1, 0)
|
||||
|
||||
|
||||
class TriTessagon(Tessagon):
|
||||
tile_class = TriTile
|
||||
metadata = metadata
|
133
add_mesh_extra_objects/tessagon/types/valemount_tessagon.py
Normal file
133
add_mesh_extra_objects/tessagon/types/valemount_tessagon.py
Normal file
@ -0,0 +1,133 @@
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, right_tile, \
|
||||
top_tile, top_left_tile, top_right_tile, \
|
||||
bottom_tile, bottom_left_tile, bottom_right_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Valemount',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['rectangles', 'squares'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class ValemountTile(Tile):
|
||||
# o--o--o--o 1,2,3,4
|
||||
# |..|.....|
|
||||
# o..o--o--o 5,6,7,8
|
||||
# ^ |..|..|..|
|
||||
# | o--o--o..o 9,10,11,12
|
||||
# | |.....|..|
|
||||
# | o--o--o--o 13,14,15,16
|
||||
# V
|
||||
# U --->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = False
|
||||
self.v_symmetric = False
|
||||
|
||||
def init_verts(self):
|
||||
# Naming stuff is hard ...
|
||||
return {1: None,
|
||||
2: None,
|
||||
3: None,
|
||||
4: None,
|
||||
5: None,
|
||||
6: None,
|
||||
7: None,
|
||||
8: None,
|
||||
9: None,
|
||||
10: None,
|
||||
11: None,
|
||||
12: None,
|
||||
13: None,
|
||||
14: None,
|
||||
15: None,
|
||||
16: None}
|
||||
|
||||
def init_faces(self):
|
||||
return {'top_left': None,
|
||||
'top_right': None,
|
||||
'bottom_left': None,
|
||||
'bottom_right': None,
|
||||
'center': None}
|
||||
|
||||
def calculate_verts(self):
|
||||
# Top row
|
||||
vert = self.add_vert([1], 0, 1)
|
||||
self.set_equivalent_vert(*left_tile(4), vert)
|
||||
self.set_equivalent_vert(*top_tile(13), vert)
|
||||
self.set_equivalent_vert(*top_left_tile(16), vert)
|
||||
|
||||
vert = self.add_vert([2], 1/3.0, 1)
|
||||
self.set_equivalent_vert(*top_tile(14), vert)
|
||||
|
||||
vert = self.add_vert([3], 2/3.0, 1)
|
||||
self.set_equivalent_vert(*top_tile(15), vert)
|
||||
|
||||
vert = self.add_vert([4], 1, 1)
|
||||
self.set_equivalent_vert(*right_tile(1), vert)
|
||||
self.set_equivalent_vert(*top_tile(16), vert)
|
||||
self.set_equivalent_vert(*top_right_tile(13), vert)
|
||||
|
||||
# Next row
|
||||
vert = self.add_vert([5], 0, 2/3.0)
|
||||
self.set_equivalent_vert(*left_tile(8), vert)
|
||||
|
||||
self.add_vert([6], 1/3.0, 2/3.0)
|
||||
self.add_vert([7], 2/3.0, 2/3.0)
|
||||
|
||||
vert = self.add_vert([8], 1, 2/3.0)
|
||||
self.set_equivalent_vert(*right_tile(5), vert)
|
||||
|
||||
# Next row
|
||||
vert = self.add_vert([9], 0, 1/3.0)
|
||||
self.set_equivalent_vert(*left_tile(12), vert)
|
||||
|
||||
self.add_vert([10], 1/3.0, 1/3.0)
|
||||
self.add_vert([11], 2/3.0, 1/3.0)
|
||||
|
||||
vert = self.add_vert([12], 1, 1/3.0)
|
||||
self.set_equivalent_vert(*right_tile(9), vert)
|
||||
|
||||
# Bottom row
|
||||
vert = self.add_vert([13], 0, 0)
|
||||
self.set_equivalent_vert(*left_tile(16), vert)
|
||||
self.set_equivalent_vert(*bottom_tile(1), vert)
|
||||
self.set_equivalent_vert(*bottom_left_tile(4), vert)
|
||||
|
||||
vert = self.add_vert([14], 1/3.0, 0)
|
||||
self.set_equivalent_vert(*bottom_tile(2), vert)
|
||||
|
||||
vert = self.add_vert([15], 2/3.0, 0)
|
||||
self.set_equivalent_vert(*bottom_tile(3), vert)
|
||||
|
||||
vert = self.add_vert([16], 1, 0)
|
||||
self.set_equivalent_vert(*right_tile(13), vert)
|
||||
self.set_equivalent_vert(*bottom_tile(4), vert)
|
||||
self.set_equivalent_vert(*bottom_right_tile(1), vert)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face('top_left',
|
||||
[1, 2, 6, 10, 9, 5])
|
||||
self.add_face('top_right',
|
||||
[2, 3, 4, 8, 7, 6])
|
||||
self.add_face('bottom_left',
|
||||
[9, 10, 11, 15, 14, 13])
|
||||
self.add_face('bottom_right',
|
||||
[7, 8, 12, 16, 15, 11])
|
||||
self.add_face('center',
|
||||
[6, 7, 11, 10])
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_paths([
|
||||
['center']
|
||||
], 1, 0)
|
||||
|
||||
|
||||
class ValemountTessagon(Tessagon):
|
||||
tile_class = ValemountTile
|
||||
metadata = metadata
|
162
add_mesh_extra_objects/tessagon/types/weave_tessagon.py
Normal file
162
add_mesh_extra_objects/tessagon/types/weave_tessagon.py
Normal file
@ -0,0 +1,162 @@
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, top_tile, left_top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Weave',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['quads', 'rectangles'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0,
|
||||
extra_parameters={
|
||||
'square_ratio': {
|
||||
'type': 'float',
|
||||
'min': 0.0,
|
||||
'max': 1.0,
|
||||
'default': 0.5,
|
||||
'description':
|
||||
'Control the size of the squares'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class WeaveTile(Tile):
|
||||
# 16 verts, 13 faces (5 internal, 8 on boundary)
|
||||
#
|
||||
# ....|..|.... .8..|.8|.8..
|
||||
# -o--o..o--o- -o--o..o--o-
|
||||
# ^ .|..|..|..|. .|4.|..|4.|.
|
||||
# | .o--o--o--o. .o--o--o--o.
|
||||
# | .|........|. 8|...8....|8
|
||||
# | .o--o--o--o. .o--o--o--o.
|
||||
# .|..|..|..|. .|4.|..|4.|.
|
||||
# V -o--o..o--o. -o--o..o--o.
|
||||
# ....|..|.... 8...|8.|...8
|
||||
#
|
||||
# U ----->
|
||||
|
||||
def __init__(self, tessagon, **kwargs):
|
||||
super().__init__(tessagon, **kwargs)
|
||||
self.u_symmetric = True
|
||||
self.v_symmetric = True
|
||||
self.square_ratio = kwargs.get('square_ratio', 0.5)
|
||||
|
||||
def init_verts(self):
|
||||
# u_square means on the square that is on the U-boundary
|
||||
return {'top': {'left': {'u_inner': {'v_inner': None,
|
||||
'v_outer': None},
|
||||
'u_outer': {'v_inner': None,
|
||||
'v_outer': None}},
|
||||
'right': {'u_inner': {'v_inner': None,
|
||||
'v_outer': None},
|
||||
'u_outer': {'v_inner': None,
|
||||
'v_outer': None}}},
|
||||
'bottom': {'left': {'u_inner': {'v_inner': None,
|
||||
'v_outer': None},
|
||||
'u_outer': {'v_inner': None,
|
||||
'v_outer': None}},
|
||||
'right': {'u_inner': {'v_inner': None,
|
||||
'v_outer': None},
|
||||
'u_outer': {'v_inner': None,
|
||||
'v_outer': None}}}}
|
||||
|
||||
def init_faces(self):
|
||||
return {'square': {'top': {'left': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'right': None}},
|
||||
'oct': {'top': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'middle': {'left': None,
|
||||
'center': None,
|
||||
'right': None},
|
||||
'bottom': {'left': None,
|
||||
'center': None,
|
||||
'right': None}}}
|
||||
|
||||
def calculate_verts(self):
|
||||
half_square_size = 0.25 * self.square_ratio
|
||||
u0 = 0.25 - half_square_size
|
||||
u1 = 0.25 + half_square_size
|
||||
|
||||
v0 = 0.75 - half_square_size
|
||||
v1 = 0.75 + half_square_size
|
||||
|
||||
# Define top left square, other verts defined through symmetry
|
||||
self.add_vert(['top', 'left', 'u_inner', 'v_inner'], u1, v0)
|
||||
self.add_vert(['top', 'left', 'u_inner', 'v_outer'], u1, v1)
|
||||
self.add_vert(['top', 'left', 'u_outer', 'v_inner'], u0, v0)
|
||||
self.add_vert(['top', 'left', 'u_outer', 'v_outer'], u0, v1)
|
||||
|
||||
def calculate_faces(self):
|
||||
# 4 internal squares (via symmetry)
|
||||
self.add_face(['square', 'top', 'left'],
|
||||
[['top', 'left', 'u_outer', 'v_outer'],
|
||||
['top', 'left', 'u_inner', 'v_outer'],
|
||||
['top', 'left', 'u_inner', 'v_inner'],
|
||||
['top', 'left', 'u_outer', 'v_inner']],
|
||||
face_type='square')
|
||||
|
||||
# 1 interior strip
|
||||
self.add_face(['oct', 'middle', 'center'],
|
||||
[['top', 'left', 'u_outer', 'v_inner'],
|
||||
['top', 'left', 'u_inner', 'v_inner'],
|
||||
['top', 'right', 'u_inner', 'v_inner'],
|
||||
['top', 'right', 'u_outer', 'v_inner'],
|
||||
['bottom', 'right', 'u_outer', 'v_inner'],
|
||||
['bottom', 'right', 'u_inner', 'v_inner'],
|
||||
['bottom', 'left', 'u_inner', 'v_inner'],
|
||||
['bottom', 'left', 'u_outer', 'v_inner']],
|
||||
face_type='oct')
|
||||
|
||||
# 4 corner strips
|
||||
self.add_face(['oct', 'top', 'left'],
|
||||
[['top', 'left', 'u_inner', 'v_outer'],
|
||||
['top', 'left', 'u_outer', 'v_outer'],
|
||||
left_tile(['top', 'right', 'u_outer', 'v_outer']),
|
||||
left_tile(['top', 'right', 'u_inner', 'v_outer']),
|
||||
left_top_tile(['bottom', 'right',
|
||||
'u_inner', 'v_outer']),
|
||||
left_top_tile(['bottom', 'right',
|
||||
'u_outer', 'v_outer']),
|
||||
top_tile(['bottom', 'left', 'u_outer', 'v_outer']),
|
||||
top_tile(['bottom', 'left', 'u_inner', 'v_outer'])],
|
||||
face_type='oct', corner=True)
|
||||
|
||||
# 2 side strips
|
||||
self.add_face(['oct', 'middle', 'left'],
|
||||
[['top', 'left', 'u_outer', 'v_outer'],
|
||||
['top', 'left', 'u_outer', 'v_inner'],
|
||||
['bottom', 'left', 'u_outer', 'v_inner'],
|
||||
['bottom', 'left', 'u_outer', 'v_outer'],
|
||||
left_tile(['bottom', 'right', 'u_outer', 'v_outer']),
|
||||
left_tile(['bottom', 'right', 'u_outer', 'v_inner']),
|
||||
left_tile(['top', 'right', 'u_outer', 'v_inner']),
|
||||
left_tile(['top', 'right', 'u_outer', 'v_outer'])],
|
||||
face_type='oct', u_boundary=True)
|
||||
|
||||
# 2 top/bottom strips
|
||||
self.add_face(['oct', 'top', 'center'],
|
||||
[['top', 'left', 'u_inner', 'v_outer'],
|
||||
['top', 'left', 'u_inner', 'v_inner'],
|
||||
['top', 'right', 'u_inner', 'v_inner'],
|
||||
['top', 'right', 'u_inner', 'v_outer'],
|
||||
top_tile(['bottom', 'right', 'u_inner', 'v_outer']),
|
||||
top_tile(['bottom', 'right', 'u_inner', 'v_inner']),
|
||||
top_tile(['bottom', 'left', 'u_inner', 'v_inner']),
|
||||
top_tile(['bottom', 'left', 'u_inner', 'v_outer'])],
|
||||
face_type='oct', v_boundary=True)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(['oct', 'top', 'center'], 1)
|
||||
self.color_face(['oct', 'middle', 'left'], 1)
|
||||
|
||||
self.color_face(['oct', 'top', 'left'], 2)
|
||||
self.color_face(['oct', 'middle', 'center'], 2)
|
||||
|
||||
|
||||
class WeaveTessagon(Tessagon):
|
||||
tile_class = WeaveTile
|
||||
metadata = metadata
|
171
add_mesh_extra_objects/tessagon/types/zig_zag_tessagon.py
Normal file
171
add_mesh_extra_objects/tessagon/types/zig_zag_tessagon.py
Normal file
@ -0,0 +1,171 @@
|
||||
from tessagon.core.tile import Tile
|
||||
from tessagon.core.tessagon import Tessagon
|
||||
from tessagon.core.tessagon_metadata import TessagonMetadata
|
||||
from tessagon.core.tile_utils import left_tile, right_tile, \
|
||||
top_tile, bottom_tile, left_top_tile, left_bottom_tile, \
|
||||
right_bottom_tile, right_top_tile
|
||||
|
||||
metadata = TessagonMetadata(name='Zig-Zag',
|
||||
num_color_patterns=1,
|
||||
classification='non_edge',
|
||||
shapes=['rectangles'],
|
||||
sides=[4],
|
||||
uv_ratio=1.0)
|
||||
|
||||
|
||||
class ZigZagTile(Tile):
|
||||
# 25 verts in five rows, five columns
|
||||
# 10 faces (2 sets shared with neighbors)
|
||||
#
|
||||
# o-o-o-o.o row5 o-o-o-o.o
|
||||
# ^ |...|.|.| |.1.|.|3|
|
||||
# | o-o-o.o-o row4 o-o-o2o-o
|
||||
# | ..|.|.|.. 4.|.|.|.6
|
||||
# | o-o.o-o-o row3 o-o5o-o-o
|
||||
# |.|.|...| |.|.|.8.|
|
||||
# V o.o-o-o-o row2 o7o-o-o-o
|
||||
# |.|.|.|.| |.|.9.|10
|
||||
# o-o-o-o.o row5 o-o-o-o.o
|
||||
#
|
||||
# 1 2 3 4 5 <-cols
|
||||
#
|
||||
# U ------>
|
||||
|
||||
def init_verts(self):
|
||||
# [col, row], these read like columns
|
||||
return {1: {1: None, 2: None, 3: None, 4: None, 5: None},
|
||||
2: {1: None, 2: None, 3: None, 4: None, 5: None},
|
||||
3: {1: None, 2: None, 3: None, 4: None, 5: None},
|
||||
4: {1: None, 2: None, 3: None, 4: None, 5: None},
|
||||
5: {1: None, 2: None, 3: None, 4: None, 5: None}}
|
||||
|
||||
def init_faces(self):
|
||||
return {1: None, 2: None, 3: None, 4: None, 5: None, 6: None,
|
||||
7: None, 8: None, 9: None, 10: None}
|
||||
|
||||
def calculate_verts(self):
|
||||
c = {1: 0.0,
|
||||
2: 1/4.0,
|
||||
3: 2/4.0,
|
||||
4: 3/4.0,
|
||||
5: 1.0}
|
||||
for col in self.verts.keys():
|
||||
for row in self.verts[col].keys():
|
||||
# Some verts only get created if neighbors exist
|
||||
if col == 5:
|
||||
if not self.get_neighbor_tile(['right']):
|
||||
if not self.get_neighbor_tile(['top']):
|
||||
if row == 5:
|
||||
continue
|
||||
if row == 4:
|
||||
continue
|
||||
if not self.get_neighbor_tile(['bottom']):
|
||||
if row == 1:
|
||||
continue
|
||||
vert = self.add_vert([col, row], c[col], c[row])
|
||||
if col == 1:
|
||||
self.set_equivalent_vert(*left_tile([5, row]), vert)
|
||||
if row == 5:
|
||||
self.set_equivalent_vert(*left_top_tile([5, 1]),
|
||||
vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*left_bottom_tile([5, 5]),
|
||||
vert)
|
||||
elif col == 5:
|
||||
self.set_equivalent_vert(*right_tile([1, row]), vert)
|
||||
if row == 5:
|
||||
self.set_equivalent_vert(*right_top_tile([1, 1]),
|
||||
vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*right_bottom_tile([1, 5]),
|
||||
vert)
|
||||
if row == 5:
|
||||
self.set_equivalent_vert(*top_tile([col, 1]), vert)
|
||||
elif row == 1:
|
||||
self.set_equivalent_vert(*bottom_tile([col, 5]), vert)
|
||||
|
||||
def calculate_faces(self):
|
||||
self.add_face(1, [[1, 5],
|
||||
[1, 4],
|
||||
[2, 4],
|
||||
[3, 4],
|
||||
[3, 5],
|
||||
[2, 5]])
|
||||
|
||||
self.add_face(2, [[3, 5],
|
||||
[3, 4],
|
||||
[3, 3],
|
||||
[4, 3],
|
||||
[4, 4],
|
||||
[4, 5]])
|
||||
|
||||
face = self.add_face(3, [[4, 5],
|
||||
[4, 4],
|
||||
[5, 4],
|
||||
[5, 5],
|
||||
top_tile([5, 2]),
|
||||
top_tile([4, 2])])
|
||||
self.set_equivalent_face(*top_tile(10), face)
|
||||
|
||||
face = self.add_face(4, [[1, 3],
|
||||
[2, 3],
|
||||
[2, 4],
|
||||
[1, 4],
|
||||
left_tile([4, 4]),
|
||||
left_tile([4, 3])])
|
||||
self.set_equivalent_face(*left_tile(6), face)
|
||||
|
||||
self.add_face(5, [[3, 2],
|
||||
[3, 3],
|
||||
[3, 4],
|
||||
[2, 4],
|
||||
[2, 3],
|
||||
[2, 2]])
|
||||
|
||||
face = self.add_face(6, [[5, 4],
|
||||
[4, 4],
|
||||
[4, 3],
|
||||
[5, 3],
|
||||
right_tile([2, 3]),
|
||||
right_tile([2, 4])])
|
||||
self.set_equivalent_face(*right_tile(4), face)
|
||||
|
||||
self.add_face(7, [[2, 1],
|
||||
[2, 2],
|
||||
[2, 3],
|
||||
[1, 3],
|
||||
[1, 2],
|
||||
[1, 1]])
|
||||
|
||||
self.add_face(8, [[5, 2],
|
||||
[5, 3],
|
||||
[4, 3],
|
||||
[3, 3],
|
||||
[3, 2],
|
||||
[4, 2]])
|
||||
|
||||
self.add_face(9, [[4, 1],
|
||||
[4, 2],
|
||||
[3, 2],
|
||||
[2, 2],
|
||||
[2, 1],
|
||||
[3, 1]])
|
||||
|
||||
face = self.add_face(10, [[5, 1],
|
||||
[5, 2],
|
||||
[4, 2],
|
||||
[4, 1],
|
||||
bottom_tile([4, 4]),
|
||||
bottom_tile([5, 4])])
|
||||
self.set_equivalent_face(*bottom_tile(3), face)
|
||||
|
||||
def color_pattern1(self):
|
||||
self.color_face(1, 1)
|
||||
self.color_face(2, 1)
|
||||
self.color_face(7, 1)
|
||||
self.color_face(8, 1)
|
||||
|
||||
|
||||
class ZigZagTessagon(Tessagon):
|
||||
tile_class = ZigZagTile
|
||||
metadata = metadata
|
1
add_mesh_extra_objects/tessagon/version.py
Normal file
1
add_mesh_extra_objects/tessagon/version.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = '0.8'
|
Loading…
Reference in New Issue
Block a user