update pyglet

This commit is contained in:
shenjack 2022-08-16 13:25:37 +08:00
parent c79a0dbbb4
commit 76d591f2d9
13 changed files with 1003 additions and 731 deletions

View File

@ -44,11 +44,14 @@ import sys
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
#: The release version #: The release version
version = '2.0.dev20' version = '2.0.dev22'
__version__ = version __version__ = version
if sys.version_info < (3, 6): MIN_PYTHON_VERSION = 3, 7
raise Exception('pyglet %s requires Python 3.6 or newer.' % version) MIN_PYTHON_VERSION_STR = '.'.join([str(v) for v in MIN_PYTHON_VERSION])
if sys.version_info < MIN_PYTHON_VERSION:
raise Exception(f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer.")
if 'sphinx' in sys.modules: if 'sphinx' in sys.modules:
setattr(sys, 'is_pyglet_doc_run', True) setattr(sys, 'is_pyglet_doc_run', True)

View File

@ -65,11 +65,13 @@ class Win32Screen(Screen):
self._handle = handle self._handle = handle
def get_matching_configs(self, template): def get_matching_configs(self, template):
canvas = Win32Canvas(self.display, 0, _user32.GetDC(0)) hdc = _user32.GetDC(0)
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas) configs = template.match(canvas)
# XXX deprecate config's being screen-specific # XXX deprecate config's being screen-specific
for config in configs: for config in configs:
config.screen = self config.screen = self
_user32.ReleaseDC(0, hdc)
return configs return configs
def get_device_name(self): def get_device_name(self):

View File

@ -35,6 +35,7 @@ except OSError as err:
_debug_font = debug_print('debug_font') _debug_font = debug_print('debug_font')
def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d): def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d):
return ord(d) << 24 | ord(c) << 16 | ord(b) << 8 | ord(a) return ord(d) << 24 | ord(c) << 16 | ord(b) << 8 | ord(a)
@ -121,13 +122,13 @@ DWRITE_MEASURING_MODE_GDI_CLASSIC = 1
DWRITE_MEASURING_MODE_GDI_NATURAL = 2 DWRITE_MEASURING_MODE_GDI_NATURAL = 2
DWRITE_GLYPH_IMAGE_FORMATS_ALL = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE | \ DWRITE_GLYPH_IMAGE_FORMATS_ALL = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE | \
DWRITE_GLYPH_IMAGE_FORMATS_CFF | \ DWRITE_GLYPH_IMAGE_FORMATS_CFF | \
DWRITE_GLYPH_IMAGE_FORMATS_COLR | \ DWRITE_GLYPH_IMAGE_FORMATS_COLR | \
DWRITE_GLYPH_IMAGE_FORMATS_SVG | \ DWRITE_GLYPH_IMAGE_FORMATS_SVG | \
DWRITE_GLYPH_IMAGE_FORMATS_PNG | \ DWRITE_GLYPH_IMAGE_FORMATS_PNG | \
DWRITE_GLYPH_IMAGE_FORMATS_JPEG | \ DWRITE_GLYPH_IMAGE_FORMATS_JPEG | \
DWRITE_GLYPH_IMAGE_FORMATS_TIFF | \ DWRITE_GLYPH_IMAGE_FORMATS_TIFF | \
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8
DWRITE_FONT_STYLE = UINT DWRITE_FONT_STYLE = UINT
DWRITE_FONT_STYLE_NORMAL = 0 DWRITE_FONT_STYLE_NORMAL = 0
@ -171,8 +172,6 @@ class D2D1_COLOR_F(Structure):
) )
class DWRITE_TEXT_METRICS(ctypes.Structure): class DWRITE_TEXT_METRICS(ctypes.Structure):
_fields_ = ( _fields_ = (
('left', FLOAT), ('left', FLOAT),
@ -316,6 +315,7 @@ class DWRITE_GLYPH_RUN(ctypes.Structure):
('bidiLevel', UINT32), ('bidiLevel', UINT32),
) )
DWRITE_SCRIPT_SHAPES = UINT DWRITE_SCRIPT_SHAPES = UINT
DWRITE_SCRIPT_SHAPES_DEFAULT = 0 DWRITE_SCRIPT_SHAPES_DEFAULT = 0
@ -1061,6 +1061,21 @@ class LegacyCollectionLoader(com.COMObject):
IID_IDWriteFactory = com.GUID(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48) IID_IDWriteFactory = com.GUID(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48)
class IDWriteRenderingParams(com.pIUnknown):
_methods_ = [
('GetGamma',
com.METHOD(FLOAT)),
('GetEnhancedContrast',
com.METHOD(FLOAT)),
('GetClearTypeLevel',
com.METHOD(FLOAT)),
('GetPixelGeometry',
com.METHOD(UINT)),
('GetRenderingMode',
com.METHOD(UINT)),
]
class IDWriteFactory(com.pIUnknown): class IDWriteFactory(com.pIUnknown):
_methods_ = [ _methods_ = [
('GetSystemFontCollection', ('GetSystemFontCollection',
@ -1078,11 +1093,11 @@ class IDWriteFactory(com.pIUnknown):
('CreateFontFace', ('CreateFontFace',
com.STDMETHOD()), com.STDMETHOD()),
('CreateRenderingParams', ('CreateRenderingParams',
com.STDMETHOD()), com.STDMETHOD(POINTER(IDWriteRenderingParams))),
('CreateMonitorRenderingParams', ('CreateMonitorRenderingParams',
com.STDMETHOD()), com.STDMETHOD()),
('CreateCustomRenderingParams', ('CreateCustomRenderingParams',
com.STDMETHOD()), com.STDMETHOD(FLOAT, FLOAT, FLOAT, UINT, UINT, POINTER(IDWriteRenderingParams))),
('RegisterFontFileLoader', ('RegisterFontFileLoader',
com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown. com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown.
('UnregisterFontFileLoader', ('UnregisterFontFileLoader',
@ -1116,7 +1131,7 @@ class IDWriteFactory1(IDWriteFactory, com.pIUnknown):
_methods_ = [ _methods_ = [
('GetEudcFontCollection', ('GetEudcFontCollection',
com.STDMETHOD()), com.STDMETHOD()),
('CreateCustomRenderingParams', ('CreateCustomRenderingParams1',
com.STDMETHOD()), com.STDMETHOD()),
] ]
@ -1139,7 +1154,7 @@ class IDWriteFactory2(IDWriteFactory1, com.pIUnknown):
com.STDMETHOD()), com.STDMETHOD()),
('TranslateColorGlyphRun', ('TranslateColorGlyphRun',
com.STDMETHOD()), com.STDMETHOD()),
('CreateCustomRenderingParams', ('CreateCustomRenderingParams2',
com.STDMETHOD()), com.STDMETHOD()),
('CreateGlyphRunAnalysis', ('CreateGlyphRunAnalysis',
com.STDMETHOD()), com.STDMETHOD()),
@ -1191,7 +1206,7 @@ class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
_methods_ = [ _methods_ = [
('CreateGlyphRunAnalysis', ('CreateGlyphRunAnalysis',
com.STDMETHOD()), com.STDMETHOD()),
('CreateCustomRenderingParams', ('CreateCustomRenderingParams3',
com.STDMETHOD()), com.STDMETHOD()),
('CreateFontFaceReference', ('CreateFontFaceReference',
com.STDMETHOD()), com.STDMETHOD()),
@ -1207,7 +1222,7 @@ class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
com.STDMETHOD()), com.STDMETHOD()),
('GetFontDownloadQueue', ('GetFontDownloadQueue',
com.STDMETHOD()), com.STDMETHOD()),
#('GetSystemFontSet', # ('GetSystemFontSet',
# com.STDMETHOD()), # com.STDMETHOD()),
] ]
@ -1220,10 +1235,12 @@ class IDWriteColorGlyphRunEnumerator1(com.pIUnknown):
com.STDMETHOD()), com.STDMETHOD()),
] ]
class IDWriteFactory4(IDWriteFactory3, com.pIUnknown): class IDWriteFactory4(IDWriteFactory3, com.pIUnknown):
_methods_ = [ _methods_ = [
('TranslateColorGlyphRun4', # Renamed to prevent clash from previous factories. ('TranslateColorGlyphRun4', # Renamed to prevent clash from previous factories.
com.STDMETHOD(D2D_POINT_2F, DWRITE_GLYPH_RUN, c_void_p, DWRITE_GLYPH_IMAGE_FORMATS, DWRITE_MEASURING_MODE, c_void_p, UINT32, POINTER(IDWriteColorGlyphRunEnumerator1))), com.STDMETHOD(D2D_POINT_2F, DWRITE_GLYPH_RUN, c_void_p, DWRITE_GLYPH_IMAGE_FORMATS, DWRITE_MEASURING_MODE,
c_void_p, UINT32, POINTER(IDWriteColorGlyphRunEnumerator1))),
('ComputeGlyphOrigins_', ('ComputeGlyphOrigins_',
com.STDMETHOD()), com.STDMETHOD()),
('ComputeGlyphOrigins', ('ComputeGlyphOrigins',
@ -1259,7 +1276,6 @@ class IDWriteFactory5(IDWriteFactory4, IDWriteFactory3, IDWriteFactory2, IDWrite
] ]
DWriteCreateFactory = dwrite_lib.DWriteCreateFactory DWriteCreateFactory = dwrite_lib.DWriteCreateFactory
DWriteCreateFactory.restype = HRESULT DWriteCreateFactory.restype = HRESULT
DWriteCreateFactory.argtypes = [DWRITE_FACTORY_TYPE, com.REFIID, POINTER(com.pIUnknown)] DWriteCreateFactory.argtypes = [DWRITE_FACTORY_TYPE, com.REFIID, POINTER(com.pIUnknown)]
@ -1424,7 +1440,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('GetTransform', ('GetTransform',
com.STDMETHOD()), com.STDMETHOD()),
('SetAntialiasMode', ('SetAntialiasMode',
com.STDMETHOD()), com.METHOD(c_void, D2D1_TEXT_ANTIALIAS_MODE)),
('GetAntialiasMode', ('GetAntialiasMode',
com.STDMETHOD()), com.STDMETHOD()),
('SetTextAntialiasMode', ('SetTextAntialiasMode',
@ -1432,7 +1448,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('GetTextAntialiasMode', ('GetTextAntialiasMode',
com.STDMETHOD()), com.STDMETHOD()),
('SetTextRenderingParams', ('SetTextRenderingParams',
com.STDMETHOD()), com.STDMETHOD(IDWriteRenderingParams)),
('GetTextRenderingParams', ('GetTextRenderingParams',
com.STDMETHOD()), com.STDMETHOD()),
('SetTags', ('SetTags',
@ -1444,7 +1460,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('PopLayer', ('PopLayer',
com.STDMETHOD()), com.STDMETHOD()),
('Flush', ('Flush',
com.STDMETHOD()), com.STDMETHOD(c_void_p, c_void_p)),
('SaveDrawingState', ('SaveDrawingState',
com.STDMETHOD()), com.STDMETHOD()),
('RestoreDrawingState', ('RestoreDrawingState',
@ -1535,6 +1551,7 @@ if not wic_decoder:
class DirectWriteGlyphRenderer(base.GlyphRenderer): class DirectWriteGlyphRenderer(base.GlyphRenderer):
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
measuring_mode = DWRITE_MEASURING_MODE_NATURAL
def __init__(self, font): def __init__(self, font):
self._render_target = None self._render_target = None
@ -1679,7 +1696,6 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False) font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False)
metrics_out = [] metrics_out = []
i = 0
for metric in glyph_metrics: for metric in glyph_metrics:
glyph_width = (metric.advanceWidth - metric.leftSideBearing - metric.rightSideBearing) glyph_width = (metric.advanceWidth - metric.leftSideBearing - metric.rightSideBearing)
@ -1696,7 +1712,6 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
advance_width = metric.advanceWidth advance_width = metric.advanceWidth
metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb)) metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb))
i += 1
return metrics_out return metrics_out
@ -1721,7 +1736,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
run, run,
None, None,
DWRITE_GLYPH_IMAGE_FORMATS_ALL, DWRITE_GLYPH_IMAGE_FORMATS_ALL,
DWRITE_MEASURING_MODE_NATURAL, self.measuring_mode,
None, None,
0, 0,
enumerator) enumerator)
@ -1735,7 +1750,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
def render_single_glyph(self, font_face, indice, advance, offset, metrics): def render_single_glyph(self, font_face, indice, advance, offset, metrics):
"""Renders a single glyph using D2D DrawGlyphRun""" """Renders a single glyph using D2D DrawGlyphRun"""
glyph_width, glyph_height, lsb, font_advance, bsb = metrics # We use a shaped advance instead of the fonts. glyph_width, glyph_height, glyph_lsb, glyph_advance, glyph_bsb = metrics # We use a shaped advance instead of the fonts.
# Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value? # Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value?
new_indice = (UINT16 * 1)(indice) new_indice = (UINT16 * 1)(indice)
@ -1748,19 +1763,24 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
new_advance, # advance, new_advance, # advance,
pointer(offset), # offset, pointer(offset), # offset,
False, False,
False 0
) )
# If it's colored, return to render it using layout. # If it's colored, return to render it using layout.
if self.draw_options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT and self.is_color_run(run): if self.draw_options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT and self.is_color_run(run):
return None return None
render_width = int(math.ceil((glyph_width) * self.font.font_scale_ratio)) # Use the glyph's advance as a width as bitmap width.
render_offset_x = int(math.floor(abs(lsb * self.font.font_scale_ratio))) # Some characters such as diacritics (̃) may have 0 advance width. In that case, just use glyph_width
if lsb < 0: if glyph_advance:
# Negative LSB: we shift the layout rect to the right render_width = int(math.ceil(glyph_advance * self.font.font_scale_ratio))
# Otherwise we will cut the left part of the glyph else:
render_offset_x = -(render_offset_x) render_width = int(math.ceil(glyph_width * self.font.font_scale_ratio))
render_offset_x = 0
if glyph_lsb < 0:
# Negative LSB: we shift the offset, otherwise the glyph will be cut off.
render_offset_x = glyph_lsb * self.font.font_scale_ratio
# Create new bitmap. # Create new bitmap.
# TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space. # TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
@ -1769,11 +1789,9 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
int(math.ceil(self.font.max_glyph_height))) int(math.ceil(self.font.max_glyph_height)))
# Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position. # Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position.
# Offsets are actually based on pixels somehow???
baseline_offset = D2D_POINT_2F(-render_offset_x - offset.advanceOffset, baseline_offset = D2D_POINT_2F(-render_offset_x - offset.advanceOffset,
self.font.ascent + offset.ascenderOffset) self.font.ascent + offset.ascenderOffset)
self._render_target.BeginDraw() self._render_target.BeginDraw()
self._render_target.Clear(transparent) self._render_target.Clear(transparent)
@ -1781,17 +1799,17 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
self._render_target.DrawGlyphRun(baseline_offset, self._render_target.DrawGlyphRun(baseline_offset,
run, run,
self._brush, self._brush,
DWRITE_MEASURING_MODE_NATURAL) self.measuring_mode)
self._render_target.EndDraw(None, None) self._render_target.EndDraw(None, None)
image = wic_decoder.get_image(self._bitmap) image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image) glyph = self.font.create_glyph(image)
glyph.set_bearings(self.font.descent, render_offset_x, glyph.set_bearings(-self.font.descent, render_offset_x,
advance * self.font.font_scale_ratio, advance,
offset.advanceOffset * self.font.font_scale_ratio, offset.advanceOffset,
offset.ascenderOffset * self.font.font_scale_ratio) offset.ascenderOffset)
return glyph return glyph
@ -1829,7 +1847,17 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
image = wic_decoder.get_image(self._bitmap) image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image) glyph = self.font.create_glyph(image)
glyph.set_bearings(self.font.descent, 0, int(math.ceil(layout_metrics.width))) glyph.set_bearings(-self.font.descent, 0, int(math.ceil(layout_metrics.width)))
return glyph
def create_zero_glyph(self):
"""Zero glyph is a 1x1 image that has a -1 advance. This is to fill in for ligature substitutions since
font system requires 1 glyph per character in a string."""
self._create_bitmap(1, 1)
image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image)
glyph.set_bearings(-self.font.descent, 0, -1)
return glyph return glyph
def _create_bitmap(self, width, height): def _create_bitmap(self, width, height):
@ -1878,6 +1906,7 @@ class Win32DirectWriteFont(base.Font):
_glyph_renderer = None _glyph_renderer = None
_empty_glyph = None _empty_glyph = None
_zero_glyph = None
glyph_renderer_class = DirectWriteGlyphRenderer glyph_renderer_class = DirectWriteGlyphRenderer
texture_internalformat = pyglet.gl.GL_RGBA texture_internalformat = pyglet.gl.GL_RGBA
@ -1973,10 +2002,10 @@ class Win32DirectWriteFont(base.Font):
self.font_scale_ratio = (self._real_size / self._font_metrics.designUnitsPerEm) self.font_scale_ratio = (self._real_size / self._font_metrics.designUnitsPerEm)
self.ascent = self._font_metrics.ascent * self.font_scale_ratio self.ascent = math.ceil(self._font_metrics.ascent * self.font_scale_ratio)
self.descent = self._font_metrics.descent * self.font_scale_ratio self.descent = -round(self._font_metrics.descent * self.font_scale_ratio)
self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio
self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio
self._fallback = None self._fallback = None
@ -1986,7 +2015,6 @@ class Win32DirectWriteFont(base.Font):
else: else:
assert _debug_font("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.") assert _debug_font("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.")
@property @property
def name(self): def name(self):
return self._name return self._name
@ -2015,21 +2043,19 @@ class Win32DirectWriteFont(base.Font):
new_glyph.set_bearings( new_glyph.set_bearings(
glyph.baseline, glyph.baseline,
glyph.lsb, glyph.lsb,
advance * self.font_scale_ratio, advance,
offset.advanceOffset * self.font_scale_ratio, offset.advanceOffset,
offset.ascenderOffset * self.font_scale_ratio offset.ascenderOffset
) )
return new_glyph return new_glyph
def _render_layout_glyph(self, text_buffer, i, clusters, check_color=True): def _render_layout_glyph(self, text_buffer, i, clusters, check_color=True):
formatted_clusters = clusters[:]
# Some glyphs can be more than 1 char. We use the clusters to determine how many of an index exist. # Some glyphs can be more than 1 char. We use the clusters to determine how many of an index exist.
text_length = formatted_clusters.count(i) text_length = clusters.count(i)
# Amount of glyphs don't always match 1:1 with text as some can be substituted or omitted. Get # Amount of glyphs don't always match 1:1 with text as some can be substituted or omitted. Get
# actual text buffer index. # actual text buffer index.
text_index = formatted_clusters.index(i) text_index = clusters.index(i)
# Get actual text based on the index and length. # Get actual text based on the index and length.
actual_text = text_buffer[text_index:text_index + text_length] actual_text = text_buffer[text_index:text_index + text_length]
@ -2125,25 +2151,48 @@ class Win32DirectWriteFont(base.Font):
if not self._glyph_renderer: if not self._glyph_renderer:
self._glyph_renderer = self.glyph_renderer_class(self) self._glyph_renderer = self.glyph_renderer_class(self)
self._empty_glyph = self._glyph_renderer.render_using_layout(" ") self._empty_glyph = self._glyph_renderer.render_using_layout(" ")
self._zero_glyph = self._glyph_renderer.create_zero_glyph()
text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text, self.font_face) text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text,
self.font_face)
metrics = self._glyph_renderer.get_glyph_metrics(self.font_face, indices, actual_count) metrics = self._glyph_renderer.get_glyph_metrics(self.font_face, indices, actual_count)
formatted_clusters = list(clusters)
# Convert to real sizes.
for i in range(actual_count):
advances[i] *= self.font_scale_ratio
for i in range(actual_count):
offsets[i].advanceOffset *= self.font_scale_ratio
offsets[i].ascenderOffset *= self.font_scale_ratio
glyphs = [] glyphs = []
# Pyglet expects 1 glyph for every string. However, ligatures can combine 1 or more glyphs, leading
# to issues with multilines producing wrong output.
substitutions = {}
for idx in clusters:
ct = formatted_clusters.count(idx)
if ct > 1:
substitutions[idx] = ct-1
for i in range(actual_count): for i in range(actual_count):
indice = indices[i] indice = indices[i]
if indice == 0: if indice == 0:
# If an indice is 0, it will return no glyph. In this case we attempt to render leveraging # If an indice is 0, it will return no glyph. In this case we attempt to render leveraging
# the built in text layout from MS. Which depending on version can use fallback fonts and other tricks # the built in text layout from MS. Which depending on version can use fallback fonts and other tricks
# to possibly get something of use. # to possibly get something of use.
glyph = self._render_layout_glyph(text_buffer, i, clusters) glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters)
glyphs.append(glyph) glyphs.append(glyph)
else: else:
advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
# Glyphs can vary depending on shaping. We will cache it by indice, advance, and offset. # Glyphs can vary depending on shaping. We will cache it by indice, advance, and offset.
# Possible to just cache without offset and set them each time. This may be faster? # Possible to just cache without offset and set them each time. This may be faster?
if indice in self.glyphs: if indice in self.glyphs:
advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
if advance_key in self._advance_cache: if advance_key in self._advance_cache:
glyph = self._advance_cache[advance_key] glyph = self._advance_cache[advance_key]
else: else:
@ -2153,14 +2202,18 @@ class Win32DirectWriteFont(base.Font):
glyph = self._glyph_renderer.render_single_glyph(self.font_face, indice, advances[i], offsets[i], glyph = self._glyph_renderer.render_single_glyph(self.font_face, indice, advances[i], offsets[i],
metrics[i]) metrics[i])
if glyph is None: # Will only return None if a color glyph is found. Use DW to render it directly. if glyph is None: # Will only return None if a color glyph is found. Use DW to render it directly.
glyph = self._render_layout_glyph(text_buffer, i, clusters, check_color=False) glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters, check_color=False)
glyph.colored = True glyph.colored = True
self.glyphs[indice] = glyph self.glyphs[indice] = glyph
self._advance_cache[(indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)] = glyph self._advance_cache[advance_key] = glyph
glyphs.append(glyph) glyphs.append(glyph)
if i in substitutions:
for _ in range(substitutions[i]):
glyphs.append(self._zero_glyph)
return glyphs return glyphs
def create_text_layout(self, text): def create_text_layout(self, text):

View File

@ -120,7 +120,18 @@ class FreeTypeGlyphRenderer(base.GlyphRenderer):
'A', 'A',
self._data, self._data,
abs(self._pitch)) abs(self._pitch))
glyph = self.font.create_glyph(img)
# HACK: Get text working in GLES until image data can be converted properly
# GLES don't support coversion during pixel transfer so we have to
# force specify the glyph format to be GL_ALPHA. This format is not
# supported in 3.3+ core, but are present in ES because of pixel transfer
# limitations.
if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
GL_ALPHA = 0x1906
glyph = self.font.create_glyph(img, fmt=GL_ALPHA)
else:
glyph = self.font.create_glyph(img)
glyph.set_bearings(self._baseline, self._lsb, self._advance_x) glyph.set_bearings(self._baseline, self._lsb, self._advance_x)
if self._pitch > 0: if self._pitch > 0:
t = list(glyph.tex_coords) t = list(glyph.tex_coords)

View File

@ -824,14 +824,14 @@ class Controller(EventDispatcher):
# Input Event types: # Input Event types:
def on_stick_motion(self, controller, axis, xvalue, yvalue): def on_stick_motion(self, controller, stick, xvalue, yvalue):
"""The value of a controller analogue stick changed. """The value of a controller analogue stick changed.
:Parameters: :Parameters:
`controller` : `Controller` `controller` : `Controller`
The controller whose analogue stick changed. The controller whose analogue stick changed.
`axis` : string `stick` : string
The name of the axis that changed. The name of the stick that changed.
`xvalue` : float `xvalue` : float
The current x axis value, normalized to [-1, 1]. The current x axis value, normalized to [-1, 1].
`yvalue` : float `yvalue` : float

View File

@ -55,12 +55,13 @@ To query which GameControllers are available, call :py:func:`get_controllers`.
.. versionadded:: 2.0 .. versionadded:: 2.0
""" """
import os import os as _os
import warnings as _warnings
from .controller_db import mapping_list from .controller_db import mapping_list
_env_config = os.environ.get('SDL_GAMECONTROLLERCONFIG') _env_config = _os.environ.get('SDL_GAMECONTROLLERCONFIG')
if _env_config: if _env_config:
# insert at the front of the list # insert at the front of the list
mapping_list.insert(0, _env_config) mapping_list.insert(0, _env_config)
@ -101,12 +102,11 @@ def _parse_mapping(mapping_string):
if ':' not in item: if ':' not in item:
continue continue
key, relation_string = item.split(':') key, relation_string, *etc = item.split(':')
if key not in valid_keys: if key not in valid_keys:
continue continue
inverted = False
# Look for specific flags to signify inverted axis: # Look for specific flags to signify inverted axis:
if "+" in relation_string: if "+" in relation_string:
relation_string = relation_string.strip('+') relation_string = relation_string.strip('+')
@ -114,9 +114,11 @@ def _parse_mapping(mapping_string):
elif "-" in relation_string: elif "-" in relation_string:
relation_string = relation_string.strip('-') relation_string = relation_string.strip('-')
inverted = True inverted = True
if "~" in relation_string: elif "~" in relation_string:
relation_string = relation_string.strip('~') relation_string = relation_string.strip('~')
inverted = True inverted = True
else:
inverted = False
# All relations will be one of (Button, Axis, or Hat). # All relations will be one of (Button, Axis, or Hat).
if relation_string.startswith("b"): # Button if relation_string.startswith("b"): # Button
@ -141,7 +143,11 @@ def get_mapping(guid):
""" """
for mapping in mapping_list: for mapping in mapping_list:
if mapping.startswith(guid): if mapping.startswith(guid):
return _parse_mapping(mapping) try:
return _parse_mapping(mapping)
except ValueError:
_warnings.warn(f"Unable to parse Controller mapping: {mapping}")
continue
def add_mappings_from_file(filename) -> None: def add_mappings_from_file(filename) -> None:

View File

@ -2,7 +2,7 @@ from pyglet import compat_platform
# This file is automatically generated by 'pyglet/tools/gen_controller_db.py' # This file is automatically generated by 'pyglet/tools/gen_controller_db.py'
# Generated on: Thu Jul 7 09:31:08 2022 # Generated on: Wed Aug 3 10:51:56 2022
if compat_platform.startswith("linux"): if compat_platform.startswith("linux"):
mapping_list = [ mapping_list = [
@ -27,6 +27,10 @@ if compat_platform.startswith("linux"):
"05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000011010000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000660000000010000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000102800000900000000010000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "05000000102800000900000000010000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -105,6 +109,8 @@ if compat_platform.startswith("linux"):
"030000000d0f00005001000009040000,HORI Fighting Commander OCTA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", "030000000d0f00005001000009040000,HORI Fighting Commander OCTA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
"030000000d0f00008400000011010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "030000000d0f00008400000011010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000011010000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000011010000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,x:b0,y:b3,",
"030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", "030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
"030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", "030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
@ -171,8 +177,26 @@ if compat_platform.startswith("linux"):
"03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,", "030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,",
"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000620000001800000,Nintendo Switch Joy-Con (L),a:b15,b:b16,guide:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b17,y:b14,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000620000001800000,Nintendo Switch Joy-Con (L),a:b16,b:b15,guide:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000620000000000000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000620000000000000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000820000000000000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000820000000000000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000720000001800000,Nintendo Switch Joy-Con (R),a:b2,b:b1,guide:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000720000001800000,Nintendo Switch Joy-Con (R),a:b1,b:b2,guide:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,", "050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", "05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,", "030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
@ -338,6 +362,8 @@ elif compat_platform.startswith("darwin"):
"03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000203800000900000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000203800000900000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000020000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -375,6 +401,8 @@ elif compat_platform.startswith("darwin"):
"03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,", "03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000010000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000000010000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f0000aa00000072050000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", "030000000d0f0000aa00000072050000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
@ -490,6 +518,8 @@ elif compat_platform.startswith("win"):
"03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000000000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", "03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -554,8 +584,6 @@ elif compat_platform.startswith("win"):
"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,", "03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,", "030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,", "78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
"03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,", "03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,",
"03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,", "03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,",
@ -582,6 +610,8 @@ elif compat_platform.startswith("win"):
"03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,", "03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
"030000000d0f00008400000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "030000000d0f00008400000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f00008500000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00008500000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000000000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
"030000000d0f00008700000000000000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",

View File

@ -46,13 +46,16 @@ for creating orthographic and perspective projection matrixes.
the object is not updated in-place. the object is not updated in-place.
""" """
from __future__ import annotations
import math as _math import math as _math
import warnings as _warnings import warnings as _warnings
from collections.abc import Iterable, Iterator
from operator import mul as _mul from operator import mul as _mul
from typing import NoReturn, TypeVar, cast, overload, Tuple
def clamp(num, min_val, max_val): def clamp(num: float, min_val: float, max_val: float) -> float:
return max(min(num, max_val), min_val) return max(min(num, max_val), min_val)
@ -70,57 +73,65 @@ class Vec2:
""" """
def __init__(self, x=0.0, y=0.0): def __init__(self, x: float = 0.0, y: float = 0.0) -> None:
self.x = x self.x = x
self.y = y self.y = y
def __iter__(self): def __iter__(self) -> Iterator[float]:
yield self.x yield self.x
yield self.y yield self.y
def __len__(self): def __len__(self) -> int:
return 2 return 2
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item): def __getitem__(self, item):
return (self.x, self.y)[item] return (self.x, self.y)[item]
def __add__(self, other): def __add__(self, other: Vec2) -> Vec2:
return Vec2(self.x + other.x, self.y + other.y) return Vec2(self.x + other.x, self.y + other.y)
def __sub__(self, other): def __sub__(self, other: Vec2) -> Vec2:
return Vec2(self.x - other.x, self.y - other.y) return Vec2(self.x - other.x, self.y - other.y)
def __mul__(self, other): def __mul__(self, other: Vec2) -> Vec2:
return Vec2(self.x * other.x, self.y * other.y) return Vec2(self.x * other.x, self.y * other.y)
def __truediv__(self, other): def __truediv__(self, other: Vec2) -> Vec2:
return Vec2(self.x / other.x, self.y / other.y) return Vec2(self.x / other.x, self.y / other.y)
def __abs__(self): def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2) return _math.sqrt(self.x ** 2 + self.y ** 2)
def __neg__(self): def __neg__(self) -> Vec2:
return Vec2(-self.x, -self.y) return Vec2(-self.x, -self.y)
def __round__(self, ndigits=None): def __round__(self, ndigits: int | None = None) -> Vec2:
return Vec2(*(round(v, ndigits) for v in self)) return Vec2(*(round(v, ndigits) for v in self))
def __radd__(self, other): def __radd__(self, other: Vec2 | int) -> Vec2:
"""Reverse add. Required for functionality with sum() """Reverse add. Required for functionality with sum()
""" """
if other == 0: if other == 0:
return self return self
else: else:
return self.__add__(other) return self.__add__(cast(Vec2, other))
def __eq__(self, other): def __eq__(self, other: object) -> bool:
return self.x == other.x and self.y == other.y return isinstance(other, Vec2) and self.x == other.x and self.y == other.y
def __ne__(self, other): def __ne__(self, other: object) -> bool:
return self.x != other.x or self.y != other.y return not isinstance(other, Vec2) or self.x != other.x or self.y != other.y
@staticmethod @staticmethod
def from_polar(mag, angle): def from_polar(mag: float, angle: float) -> Vec2:
"""Create a new vector from the given polar coordinates. """Create a new vector from the given polar coordinates.
:parameters: :parameters:
@ -134,7 +145,7 @@ class Vec2:
""" """
return Vec2(mag * _math.cos(angle), mag * _math.sin(angle)) return Vec2(mag * _math.cos(angle), mag * _math.sin(angle))
def from_magnitude(self, magnitude): def from_magnitude(self, magnitude: float) -> Vec2:
"""Create a new Vector of the given magnitude by normalizing, """Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The heading remains unchanged. then scaling the vector. The heading remains unchanged.
@ -147,7 +158,7 @@ class Vec2:
""" """
return self.normalize().scale(magnitude) return self.normalize().scale(magnitude)
def from_heading(self, heading): def from_heading(self, heading: float) -> Vec2:
"""Create a new vector of the same magnitude with the given heading. I.e. Rotate the vector to the heading. """Create a new vector of the same magnitude with the given heading. I.e. Rotate the vector to the heading.
:parameters: :parameters:
@ -161,7 +172,7 @@ class Vec2:
return Vec2(mag * _math.cos(heading), mag * _math.sin(heading)) return Vec2(mag * _math.cos(heading), mag * _math.sin(heading))
@property @property
def heading(self): def heading(self) -> float:
"""The angle of the vector in radians. """The angle of the vector in radians.
:type: float :type: float
@ -169,7 +180,7 @@ class Vec2:
return _math.atan2(self.y, self.x) return _math.atan2(self.y, self.x)
@property @property
def mag(self): def mag(self) -> float:
"""The magnitude, or length of the vector. The distance between the coordinates and the origin. """The magnitude, or length of the vector. The distance between the coordinates and the origin.
Alias of abs(self). Alias of abs(self).
@ -178,7 +189,7 @@ class Vec2:
""" """
return self.__abs__() return self.__abs__()
def limit(self, maximum): def limit(self, maximum: float) -> Vec2:
"""Limit the magnitude of the vector to the value used for the max parameter. """Limit the magnitude of the vector to the value used for the max parameter.
:parameters: :parameters:
@ -192,12 +203,12 @@ class Vec2:
return self.from_magnitude(maximum) return self.from_magnitude(maximum)
return self return self
def lerp(self, other, alpha): def lerp(self, other: Vec2, alpha: float) -> Vec2:
"""Create a new vector lineraly interpolated between this vector and another vector. """Create a new Vec2 linearly interpolated between this vector and another Vec2.
:parameters: :parameters:
`other` : Vec2 : `other` : Vec2 :
The vector to be linerly interpolated to. The vector to linearly interpolate with.
`alpha` : float or int : `alpha` : float or int :
The amount of interpolation. The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector). Some value between 0.0 (this vector) and 1.0 (other vector).
@ -209,19 +220,19 @@ class Vec2:
return Vec2(self.x + (alpha * (other.x - self.x)), return Vec2(self.x + (alpha * (other.x - self.x)),
self.y + (alpha * (other.y - self.y))) self.y + (alpha * (other.y - self.y)))
def scale(self, value): def scale(self, value: float) -> Vec2:
"""Multiply the vector by a scalar value. """Multiply the vector by a scalar value.
:parameters: :parameters:
`value` : int or float : `value` : int or float :
The ammount to be scaled by The value to scale the vector by.
:returns: A new vector scaled by the value. :returns: A new vector scaled by the value.
:rtype: Vec2 :rtype: Vec2
""" """
return Vec2(self.x * value, self.y * value) return Vec2(self.x * value, self.y * value)
def rotate(self, angle): def rotate(self, angle: float) -> Vec2:
"""Create a new Vector rotated by the angle. The magnitude remains unchanged. """Create a new Vector rotated by the angle. The magnitude remains unchanged.
:parameters: :parameters:
@ -233,9 +244,9 @@ class Vec2:
""" """
mag = self.mag mag = self.mag
heading = self.heading heading = self.heading
return Vec2(mag * _math.cos(heading + angle), mag * _math.sin(heading+angle)) return Vec2(mag * _math.cos(heading + angle), mag * _math.sin(heading + angle))
def distance(self, other): def distance(self, other: Vec2) -> float:
"""Calculate the distance between this vector and another 2D vector. """Calculate the distance between this vector and another 2D vector.
:parameters: :parameters:
@ -247,7 +258,7 @@ class Vec2:
""" """
return _math.sqrt(((other.x - self.x) ** 2) + ((other.y - self.y) ** 2)) return _math.sqrt(((other.x - self.x) ** 2) + ((other.y - self.y) ** 2))
def normalize(self): def normalize(self) -> Vec2:
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector. """Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
:returns: A unit vector with the same heading. :returns: A unit vector with the same heading.
@ -258,7 +269,7 @@ class Vec2:
return Vec2(self.x / d, self.y / d) return Vec2(self.x / d, self.y / d)
return self return self
def clamp(self, min_val, max_val): def clamp(self, min_val: float, max_val: float) -> Vec2:
"""Restrict the value of the X and Y components of the vector to be within the given values. """Restrict the value of the X and Y components of the vector to be within the given values.
:parameters: :parameters:
@ -272,7 +283,7 @@ class Vec2:
""" """
return Vec2(clamp(self.x, min_val, max_val), clamp(self.y, min_val, max_val)) return Vec2(clamp(self.x, min_val, max_val), clamp(self.y, min_val, max_val))
def dot(self, other): def dot(self, other: Vec2) -> float:
"""Calculate the dot product of this vector and another 2D vector. """Calculate the dot product of this vector and another 2D vector.
:parameters: :parameters:
@ -284,15 +295,17 @@ class Vec2:
""" """
return self.x * other.x + self.y * other.y return self.x * other.x + self.y * other.y
def __getattr__(self, attrs): def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try: try:
# Allow swizzed getting of attrs # Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs)) vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xy'.index(c)] for c in attrs)) return vec_class(*(self['xy'.index(c)] for c in attrs))
except Exception: except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'") raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self): def __repr__(self) -> str:
return f"Vec2({self.x}, {self.y})" return f"Vec2({self.x}, {self.y})"
@ -312,24 +325,32 @@ class Vec3:
""" """
def __init__(self, x=0.0, y=0.0, z=0.0): def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0) -> None:
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
def __iter__(self): def __iter__(self) -> Iterator[float]:
yield self.x yield self.x
yield self.y yield self.y
yield self.z yield self.z
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item): def __getitem__(self, item):
return (self.x, self.y, self.z)[item] return (self.x, self.y, self.z)[item]
def __len__(self): def __len__(self) -> int:
return 3 return 3
@property @property
def mag(self): def mag(self) -> float:
"""The magnitude, or length of the vector. The distance between the coordinates and the origin. """The magnitude, or length of the vector. The distance between the coordinates and the origin.
Alias of abs(self). Alias of abs(self).
@ -338,42 +359,42 @@ class Vec3:
""" """
return self.__abs__() return self.__abs__()
def __add__(self, other): def __add__(self, other: Vec3) -> Vec3:
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z) return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other): def __sub__(self, other: Vec3) -> Vec3:
return Vec3(self.x - other.x, self.y - other.y, self.z - other.z) return Vec3(self.x - other.x, self.y - other.y, self.z - other.z)
def __mul__(self, other): def __mul__(self, other: Vec3) -> Vec3:
return Vec3(self.x * other.x, self.y * other.y, self.z * other.z) return Vec3(self.x * other.x, self.y * other.y, self.z * other.z)
def __truediv__(self, other): def __truediv__(self, other: Vec3) -> Vec3:
return Vec3(self.x / other.x, self.y / other.y, self.z / other.z) return Vec3(self.x / other.x, self.y / other.y, self.z / other.z)
def __abs__(self): def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2) return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
def __neg__(self): def __neg__(self) -> Vec3:
return Vec3(-self.x, -self.y, -self.z) return Vec3(-self.x, -self.y, -self.z)
def __round__(self, ndigits=None): def __round__(self, ndigits: int | None = None) -> Vec3:
return Vec3(*(round(v, ndigits) for v in self)) return Vec3(*(round(v, ndigits) for v in self))
def __radd__(self, other): def __radd__(self, other: Vec3 | int) -> Vec3:
"""Reverse add. Required for functionality with sum() """Reverse add. Required for functionality with sum()
""" """
if other == 0: if other == 0:
return self return self
else: else:
return self.__add__(other) return self.__add__(cast(Vec3, other))
def __eq__(self, other): def __eq__(self, other: object) -> bool:
return self.x == other.x and self.y == other.y and self.z == other.z return isinstance(object, Vec3) and self.x == other.x and self.y == other.y and self.z == other.z
def __ne__(self, other): def __ne__(self, other: object) -> bool:
return self.x != other.x or self.y != other.y or self.z != other.z return not isinstance(object, Vec3) or self.x != other.x or self.y != other.y or self.z != other.z
def from_magnitude(self, magnitude): def from_magnitude(self, magnitude: float) -> Vec3:
"""Create a new Vector of the given magnitude by normalizing, """Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The rotation remains unchanged. then scaling the vector. The rotation remains unchanged.
@ -386,7 +407,7 @@ class Vec3:
""" """
return self.normalize().scale(magnitude) return self.normalize().scale(magnitude)
def limit(self, maximum): def limit(self, maximum: float) -> Vec3:
"""Limit the magnitude of the vector to the value used for the max parameter. """Limit the magnitude of the vector to the value used for the max parameter.
:parameters: :parameters:
@ -397,10 +418,10 @@ class Vec3:
:rtype: Vec3 :rtype: Vec3
""" """
if self.x ** 2 + self.y ** 2 + self.z ** 2 > maximum * maximum * maximum: if self.x ** 2 + self.y ** 2 + self.z ** 2 > maximum * maximum * maximum:
return self.from_magnitude(max) return self.from_magnitude(maximum)
return self return self
def cross(self, other): def cross(self, other: Vec3) -> Vec3:
"""Calculate the cross product of this vector and another 3D vector. """Calculate the cross product of this vector and another 3D vector.
:parameters: :parameters:
@ -414,7 +435,7 @@ class Vec3:
(self.z * other.x) - (self.x * other.z), (self.z * other.x) - (self.x * other.z),
(self.x * other.y) - (self.y * other.x)) (self.x * other.y) - (self.y * other.x))
def dot(self, other): def dot(self, other: Vec3) -> float:
"""Calculate the dot product of this vector and another 3D vector. """Calculate the dot product of this vector and another 3D vector.
:parameters: :parameters:
@ -426,12 +447,12 @@ class Vec3:
""" """
return self.x * other.x + self.y * other.y + self.z * other.z return self.x * other.x + self.y * other.y + self.z * other.z
def lerp(self, other, alpha): def lerp(self, other: Vec3, alpha: float) -> Vec3:
"""Create a new vector lineraly interpolated between this vector and another vector. """Create a new Vec3 linearly interpolated between this vector and another Vec3.
:parameters: :parameters:
`other` : Vec3 : `other` : Vec3 :
The vector to be linerly interpolated to. The vector to linearly interpolate with.
`alpha` : float or int : `alpha` : float or int :
The amount of interpolation. The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector). Some value between 0.0 (this vector) and 1.0 (other vector).
@ -444,19 +465,19 @@ class Vec3:
self.y + (alpha * (other.y - self.y)), self.y + (alpha * (other.y - self.y)),
self.z + (alpha * (other.z - self.z))) self.z + (alpha * (other.z - self.z)))
def scale(self, value): def scale(self, value: float) -> Vec3:
"""Multiply the vector by a scalar value. """Multiply the vector by a scalar value.
:parameters: :parameters:
`value` : int or float : `value` : int or float :
The ammount to be scaled by The value to scale the vector by.
:returns: A new vector scaled by the value. :returns: A new vector scaled by the value.
:rtype: Vec3 :rtype: Vec3
""" """
return Vec3(self.x * value, self.y * value, self.z * value) return Vec3(self.x * value, self.y * value, self.z * value)
def distance(self, other): def distance(self, other: Vec3) -> float:
"""Calculate the distance between this vector and another 3D vector. """Calculate the distance between this vector and another 3D vector.
:parameters: :parameters:
@ -470,7 +491,7 @@ class Vec3:
((other.y - self.y) ** 2) + ((other.y - self.y) ** 2) +
((other.z - self.z) ** 2)) ((other.z - self.z) ** 2))
def normalize(self): def normalize(self) -> Vec3:
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector. """Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
:returns: A unit vector with the same rotation. :returns: A unit vector with the same rotation.
@ -481,7 +502,7 @@ class Vec3:
return Vec3(self.x / d, self.y / d, self.z / d) return Vec3(self.x / d, self.y / d, self.z / d)
return self return self
def clamp(self, min_val, max_val): def clamp(self, min_val: float, max_val: float) -> Vec3:
"""Restrict the value of the X, Y and Z components of the vector to be within the given values. """Restrict the value of the X, Y and Z components of the vector to be within the given values.
:parameters: :parameters:
@ -497,15 +518,17 @@ class Vec3:
clamp(self.y, min_val, max_val), clamp(self.y, min_val, max_val),
clamp(self.z, min_val, max_val)) clamp(self.z, min_val, max_val))
def __getattr__(self, attrs): def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try: try:
# Allow swizzed getting of attrs # Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs)) vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xyz'.index(c)] for c in attrs)) return vec_class(*(self['xyz'.index(c)] for c in attrs))
except Exception: except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'") raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self): def __repr__(self) -> str:
return f"Vec3({self.x}, {self.y}, {self.z})" return f"Vec3({self.x}, {self.y}, {self.z})"
@ -527,100 +550,144 @@ class Vec4:
""" """
def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0): def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0, w: float = 0.0) -> None:
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
self.w = w self.w = w
def __iter__(self): def __iter__(self) -> Iterator[float]:
yield self.x yield self.x
yield self.y yield self.y
yield self.z yield self.z
yield self.w yield self.w
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item): def __getitem__(self, item):
return (self.x, self.y, self.z, self.w)[item] return (self.x, self.y, self.z, self.w)[item]
def __len__(self): def __len__(self) -> int:
return 4 return 4
def __add__(self, other): def __add__(self, other: Vec4) -> Vec4:
return Vec4(self.x + other.x, self.y + other.y, self.z + other.z, self.w + other.w) return Vec4(self.x + other.x, self.y + other.y, self.z + other.z, self.w + other.w)
def __sub__(self, other): def __sub__(self, other: Vec4) -> Vec4:
return Vec4(self.x - other.x, self.y - other.y, self.z - other.z, self.w - other.w) return Vec4(self.x - other.x, self.y - other.y, self.z - other.z, self.w - other.w)
def __mul__(self, other): def __mul__(self, other: Vec4) -> Vec4:
return Vec4(self.x * other.x, self.y * other.y, self.z * other.z, self.w * other.w) return Vec4(self.x * other.x, self.y * other.y, self.z * other.z, self.w * other.w)
def __truediv__(self, other): def __truediv__(self, other: Vec4) -> Vec4:
return Vec4(self.x / other.x, self.y / other.y, self.z / other.z, self.w / other.w) return Vec4(self.x / other.x, self.y / other.y, self.z / other.z, self.w / other.w)
def __abs__(self): def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2 + self.w ** 2) return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2 + self.w ** 2)
def __neg__(self): def __neg__(self) -> Vec4:
return Vec4(-self.x, -self.y, -self.z, -self.w) return Vec4(-self.x, -self.y, -self.z, -self.w)
def __round__(self, ndigits=None): def __round__(self, ndigits: int | None = None) -> Vec4:
return Vec4(*(round(v, ndigits) for v in self)) return Vec4(*(round(v, ndigits) for v in self))
def __radd__(self, other): def __radd__(self, other: Vec4 | int) -> Vec4:
if other == 0: if other == 0:
return self return self
else: else:
return self.__add__(other) return self.__add__(cast(Vec4, other))
def __eq__(self, other): def __eq__(self, other: object) -> bool:
return self.x == other.x and self.y == other.y and self.z == other.z and self.w == other.w return (
isinstance(other, Vec4)
and self.x == other.x
and self.y == other.y
and self.z == other.z
and self.w == other.w
)
def __ne__(self, other): def __ne__(self, other: object) -> bool:
return self.x != other.x or self.y != other.y or self.z != other.z or self.w != other.w return (
not isinstance(other, Vec4)
or self.x != other.x
or self.y != other.y
or self.z != other.z
or self.w != other.w
)
def lerp(self, other, alpha): def lerp(self, other: Vec4, alpha: float) -> Vec4:
"""Create a new Vec4 linearly interpolated between this one and another Vec4.
:parameters:
`other` : Vec4 :
The vector to linearly interpolate with.
`alpha` : float or int :
The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector).
0.5 is halfway inbetween.
:returns: A new interpolated vector.
:rtype: Vec4
"""
return Vec4(self.x + (alpha * (other.x - self.x)), return Vec4(self.x + (alpha * (other.x - self.x)),
self.y + (alpha * (other.y - self.y)), self.y + (alpha * (other.y - self.y)),
self.z + (alpha * (other.z - self.z)), self.z + (alpha * (other.z - self.z)),
self.w + (alpha * (other.w - self.w))) self.w + (alpha * (other.w - self.w)))
def scale(self, value): def scale(self, value: float) -> Vec4:
"""Multiply the vector by a scalar value.
:parameters:
`value` : int or float :
The value to scale the vector by.
:returns: A new vector scaled by the value.
:rtype: Vec4
"""
return Vec4(self.x * value, self.y * value, self.z * value, self.w * value) return Vec4(self.x * value, self.y * value, self.z * value, self.w * value)
def distance(self, other): def distance(self, other: Vec4) -> float:
return _math.sqrt(((other.x - self.x) ** 2) + return _math.sqrt(((other.x - self.x) ** 2) +
((other.y - self.y) ** 2) + ((other.y - self.y) ** 2) +
((other.z - self.z) ** 2) + ((other.z - self.z) ** 2) +
((other.w - self.w) ** 2)) ((other.w - self.w) ** 2))
def normalize(self): def normalize(self) -> Vec4:
d = self.__abs__() d = self.__abs__()
if d: if d:
return Vec4(self.x / d, self.y / d, self.z / d, self.w / d) return Vec4(self.x / d, self.y / d, self.z / d, self.w / d)
return self return self
def clamp(self, min_val, max_val): def clamp(self, min_val: float, max_val: float) -> Vec4:
return Vec4(clamp(self.x, min_val, max_val), return Vec4(clamp(self.x, min_val, max_val),
clamp(self.y, min_val, max_val), clamp(self.y, min_val, max_val),
clamp(self.z, min_val, max_val), clamp(self.z, min_val, max_val),
clamp(self.w, min_val, max_val)) clamp(self.w, min_val, max_val))
def dot(self, other): def dot(self, other: Vec4) -> float:
return self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w return self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
def __getattr__(self, attrs): def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try: try:
# Allow swizzed getting of attrs # Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs)) vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xyzw'.index(c)] for c in attrs)) return vec_class(*(self['xyzw'.index(c)] for c in attrs))
except Exception: except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'") raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self): def __repr__(self) -> str:
return f"Vec4({self.x}, {self.y}, {self.z}, {self.w})" return f"Vec4({self.x}, {self.y}, {self.z}, {self.w})"
class Mat3(tuple): class Mat3(Tuple[float, float, float, float, float, float, float, float, float]):
"""A 3x3 Matrix class """A 3x3 Matrix class
`Mat3` is an immutable 3x3 Matrix, including most common `Mat3` is an immutable 3x3 Matrix, including most common
@ -628,7 +695,9 @@ class Mat3(tuple):
the "@" operator. the "@" operator.
""" """
def __new__(cls, values=None) -> 'Mat3': def __new__(
cls, values: Iterable[float] = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)
) -> Mat3:
"""Create a 3x3 Matrix """Create a 3x3 Matrix
A Mat3 can be created with a list or tuple of 9 values. A Mat3 can be created with a list or tuple of 9 values.
@ -640,49 +709,56 @@ class Mat3(tuple):
`values` : tuple of float or int `values` : tuple of float or int
A tuple or list containing 9 floats or ints. A tuple or list containing 9 floats or ints.
""" """
assert values is None or len(values) == 9, "A 3x3 Matrix requires 9 values" new = super().__new__(Mat3, values)
return super().__new__(Mat3, values or (1.0, 0.0, 0.0, assert len(new) == 9, "A 3x3 Matrix requires 9 values"
0.0, 1.0, 0.0, return new
0.0, 0.0, 1.0))
def scale(self, sx: float, sy: float): def scale(self, sx: float, sy: float) -> Mat3:
return self @ (1.0 / sx, 0.0, 0.0, 0.0, 1.0 / sy, 0.0, 0.0, 0.0, 1.0) return self @ Mat3((1.0 / sx, 0.0, 0.0, 0.0, 1.0 / sy, 0.0, 0.0, 0.0, 1.0))
def translate(self, tx: float, ty: float): def translate(self, tx: float, ty: float) -> Mat3:
return self @ (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -tx, ty, 1.0) return self @ Mat3((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -tx, ty, 1.0))
def rotate(self, phi: float): def rotate(self, phi: float) -> Mat3:
s = _math.sin(_math.radians(phi)) s = _math.sin(_math.radians(phi))
c = _math.cos(_math.radians(phi)) c = _math.cos(_math.radians(phi))
return self @ (c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0) return self @ Mat3((c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0))
def shear(self, sx: float, sy: float): def shear(self, sx: float, sy: float) -> Mat3:
return self @ (1.0, sy, 0.0, sx, 1.0, 0.0, 0.0, 0.0, 1.0) return self @ Mat3((1.0, sy, 0.0, sx, 1.0, 0.0, 0.0, 0.0, 1.0))
def __add__(self, other) -> 'Mat3': def __add__(self, other: Mat3) -> Mat3:
assert len(other) == 9, "Can only add to other Mat3 types" if not isinstance(other, Mat3):
return Mat3(tuple(s + o for s, o in zip(self, other))) raise TypeError("Can only add to other Mat3 types")
return Mat3(s + o for s, o in zip(self, other))
def __sub__(self, other) -> 'Mat3': def __sub__(self, other: Mat3) -> Mat3:
assert len(other) == 9, "Can only subtract from other Mat3 types" if not isinstance(other, Mat3):
return Mat3(tuple(s - o for s, o in zip(self, other))) raise TypeError("Can only subtract from other Mat3 types")
return Mat3(s - o for s, o in zip(self, other))
def __pos__(self): def __pos__(self) -> Mat3:
return self return self
def __neg__(self) -> 'Mat3': def __neg__(self) -> Mat3:
return Mat3(tuple(-v for v in self)) return Mat3(-v for v in self)
def __round__(self, ndigits=None) -> 'Mat3': def __round__(self, ndigits: int | None = None) -> Mat3:
return Mat3(tuple(round(v, ndigits) for v in self)) return Mat3(round(v, ndigits) for v in self)
def __mul__(self, other): def __mul__(self, other: object) -> NoReturn:
raise NotImplementedError("Please use the @ operator for Matrix multiplication.") raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
def __matmul__(self, other) -> 'Mat3' or 'Vec3': @overload
assert len(other) in (3, 9), "Can only multiply with Mat3 or Vec3 types" def __matmul__(self, other: Vec3) -> Vec3:
...
if type(other) is Vec3: @overload
def __matmul__(self, other: Mat3) -> Mat3:
...
def __matmul__(self, other):
if isinstance(other, Vec3):
# Columns: # Columns:
c0 = self[0::3] c0 = self[0::3]
c1 = self[1::3] c1 = self[1::3]
@ -691,6 +767,9 @@ class Mat3(tuple):
sum(map(_mul, c1, other)), sum(map(_mul, c1, other)),
sum(map(_mul, c2, other))) sum(map(_mul, c2, other)))
if not isinstance(other, Mat3):
raise TypeError("Can only multiply with Mat3 or Vec3 types")
# Rows: # Rows:
r0 = self[0:3] r0 = self[0:3]
r1 = self[3:6] r1 = self[3:6]
@ -717,7 +796,17 @@ class Mat3(tuple):
return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}" return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}"
class Mat4(tuple): Mat4T = TypeVar("Mat4T", bound="Mat4")
class Mat4(
Tuple[
float, float, float, float,
float, float, float, float,
float, float, float, float,
float, float, float, float,
]
):
"""A 4x4 Matrix class """A 4x4 Matrix class
`Mat4` is an immutable 4x4 Matrix, including most common `Mat4` is an immutable 4x4 Matrix, including most common
@ -727,7 +816,15 @@ class Mat4(tuple):
and perspective projections matrixes. and perspective projections matrixes.
""" """
def __new__(cls, values=None) -> 'Mat4': def __new__(
cls,
values: Iterable[float] = (
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
),
) -> Mat4:
"""Create a 4x4 Matrix """Create a 4x4 Matrix
A Matrix can be created with a list or tuple of 16 values. A Matrix can be created with a list or tuple of 16 values.
@ -739,14 +836,21 @@ class Mat4(tuple):
`values` : tuple of float or int `values` : tuple of float or int
A tuple or list containing 16 floats or ints. A tuple or list containing 16 floats or ints.
""" """
assert values is None or len(values) == 16, "A 4x4 Matrix requires 16 values"
return super().__new__(Mat4, values or (1.0, 0.0, 0.0, 0.0, new = super().__new__(Mat4, values)
0.0, 1.0, 0.0, 0.0, assert len(new) == 16, "A 4x4 Matrix requires 16 values"
0.0, 0.0, 1.0, 0.0, return new
0.0, 0.0, 0.0, 1.0))
@classmethod @classmethod
def orthogonal_projection(cls, left, right, bottom, top, z_near, z_far) -> 'Mat4': def orthogonal_projection(
cls: type[Mat4T],
left: float,
right: float,
bottom: float,
top: float,
z_near: float,
z_far: float
) -> Mat4T:
"""Create a Mat4 orthographic projection matrix.""" """Create a Mat4 orthographic projection matrix."""
width = right - left width = right - left
height = top - bottom height = top - bottom
@ -766,7 +870,13 @@ class Mat4(tuple):
tx, ty, tz, 1.0)) tx, ty, tz, 1.0))
@classmethod @classmethod
def perspective_projection(cls, aspect, z_near, z_far, fov=60) -> 'Mat4': def perspective_projection(
cls: type[Mat4T],
aspect: float,
z_near: float,
z_far: float,
fov: float = 60
) -> Mat4T:
""" """
Create a Mat4 perspective projection matrix. Create a Mat4 perspective projection matrix.
@ -796,7 +906,7 @@ class Mat4(tuple):
0, 0, qn, 0)) 0, 0, qn, 0))
@classmethod @classmethod
def from_translation(cls, vector: Vec3) -> 'Mat4': def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T:
"""Create a translation matrix from a Vec3. """Create a translation matrix from a Vec3.
:Parameters: :Parameters:
@ -809,7 +919,7 @@ class Mat4(tuple):
vector[0], vector[1], vector[2], 1.0)) vector[0], vector[1], vector[2], 1.0))
@classmethod @classmethod
def from_rotation(cls, angle: float, vector: Vec3) -> 'Mat4': def from_rotation(cls, angle: float, vector: Vec3) -> Mat4:
"""Create a rotation matrix from an angle and Vec3. """Create a rotation matrix from an angle and Vec3.
:Parameters: :Parameters:
@ -821,10 +931,10 @@ class Mat4(tuple):
return cls().rotate(angle, vector) return cls().rotate(angle, vector)
@classmethod @classmethod
def look_at_direction(cls, direction: Vec3, up: Vec3) -> 'Mat4': def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
vec_z = direction.normalize() vec_z = direction.normalize()
vec_x = direction.cross_product(up).normalize() vec_x = direction.cross(up).normalize()
vec_y = direction.cross_product(vec_z).normalize() vec_y = direction.cross(vec_z).normalize()
return cls((vec_x.x, vec_y.x, vec_z.x, 0.0, return cls((vec_x.x, vec_y.x, vec_z.x, 0.0,
vec_x.y, vec_y.y, vec_z.y, 0.0, vec_x.y, vec_y.y, vec_z.y, 0.0,
@ -832,21 +942,21 @@ class Mat4(tuple):
0.0, 0.0, 0.0, 1.0)) 0.0, 0.0, 0.0, 1.0))
@classmethod @classmethod
def look_at(cls, position: Vec3, target: Vec3, up: Vec3) -> 'Mat4': def look_at(cls, position: Vec3, target: Vec3, up: Vec3) -> Mat4:
direction = target - position direction = target - position
direction_mat4 = cls.look_at_direction(direction, up) direction_mat4 = cls.look_at_direction(direction, up)
position_mat4 = cls.from_translation(position.negate()) position_mat4 = cls.from_translation(-position)
return direction_mat4 @ position_mat4 return direction_mat4 @ position_mat4
def row(self, index: int): def row(self, index: int) -> tuple:
"""Get a specific row as a tuple.""" """Get a specific row as a tuple."""
return self[index*4:index*4+4] return self[index * 4 : index * 4 + 4]
def column(self, index: int): def column(self, index: int) -> tuple:
"""Get a specific column as a tuple.""" """Get a specific column as a tuple."""
return self[index::4] return self[index::4]
def scale(self, vector: Vec3) -> 'Mat4': def scale(self, vector: Vec3) -> Mat4:
"""Get a scale Matrix on x, y, or z axis.""" """Get a scale Matrix on x, y, or z axis."""
temp = list(self) temp = list(self)
temp[0] *= vector[0] temp[0] *= vector[0]
@ -854,13 +964,14 @@ class Mat4(tuple):
temp[10] *= vector[2] temp[10] *= vector[2]
return Mat4(temp) return Mat4(temp)
def translate(self, vector: Vec3) -> 'Mat4': def translate(self, vector: Vec3) -> Mat4:
"""Get a translation Matrix along x, y, and z axis.""" """Get a translation Matrix along x, y, and z axis."""
return Mat4(self) @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1)) return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
def rotate(self, angle: float, vector: Vec3) -> 'Mat4': def rotate(self, angle: float, vector: Vec3) -> Mat4:
"""Get a rotation Matrix on x, y, or z axis.""" """Get a rotation Matrix on x, y, or z axis."""
assert all(abs(n) <= 1 for n in vector), "vector must be normalized (<=1)" if not all(abs(n) <= 1 for n in vector):
raise ValueError("vector must be normalized (<=1)")
x, y, z = vector x, y, z = vector
c = _math.cos(angle) c = _math.cos(angle)
s = _math.sin(angle) s = _math.sin(angle)
@ -884,25 +995,27 @@ class Mat4(tuple):
return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1)) return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1))
def transpose(self) -> 'Mat4': def transpose(self) -> Mat4:
"""Get a transpose of this Matrix.""" """Get a transpose of this Matrix."""
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4]) return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
def __add__(self, other) -> 'Mat4': def __add__(self, other: Mat4) -> Mat4:
assert len(other) == 16, "Can only add to other Mat4 types" if not isinstance(other, Mat4):
return Mat4(tuple(s + o for s, o in zip(self, other))) raise TypeError("Can only add to other Mat4 types")
return Mat4(s + o for s, o in zip(self, other))
def __sub__(self, other) -> 'Mat4': def __sub__(self, other: Mat4) -> Mat4:
assert len(other) == 16, "Can only subtract from other Mat4 types" if not isinstance(other, Mat4):
return Mat4(tuple(s - o for s, o in zip(self, other))) raise TypeError("Can only subtract from other Mat4 types")
return Mat4(s - o for s, o in zip(self, other))
def __pos__(self): def __pos__(self) -> Mat4:
return self return self
def __neg__(self) -> 'Mat4': def __neg__(self) -> Mat4:
return Mat4(tuple(-v for v in self)) return Mat4(-v for v in self)
def __invert__(self) -> 'Mat4': def __invert__(self) -> Mat4:
a = self[10] * self[15] - self[11] * self[14] a = self[10] * self[15] - self[11] * self[14]
b = self[9] * self[15] - self[11] * self[13] b = self[9] * self[15] - self[11] * self[13]
c = self[9] * self[14] - self[10] * self[13] c = self[9] * self[14] - self[10] * self[13]
@ -951,16 +1064,22 @@ class Mat4(tuple):
ndet * (self[0] * i - self[1] * n + self[2] * q), ndet * (self[0] * i - self[1] * n + self[2] * q),
pdet * (self[0] * l - self[1] * p + self[2] * r))) pdet * (self[0] * l - self[1] * p + self[2] * r)))
def __round__(self, ndigits=None) -> 'Mat4': def __round__(self, ndigits: int | None = None) -> Mat4:
return Mat4(tuple(round(v, ndigits) for v in self)) return Mat4(round(v, ndigits) for v in self)
def __mul__(self, other): def __mul__(self, other: int) -> NoReturn:
raise NotImplementedError("Please use the @ operator for Matrix multiplication.") raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
def __matmul__(self, other) -> 'Mat4' or 'Vec4': @overload
assert len(other) in (4, 16), "Can only multiply with Mat4 or Vec4 types" def __matmul__(self, other: Vec4) -> Vec4:
...
if type(other) is Vec4: @overload
def __matmul__(self, other: Mat4) -> Mat4:
...
def __matmul__(self, other):
if isinstance(other, Vec4):
# Columns: # Columns:
c0 = self[0::4] c0 = self[0::4]
c1 = self[1::4] c1 = self[1::4]
@ -971,6 +1090,8 @@ class Mat4(tuple):
sum(map(_mul, c2, other)), sum(map(_mul, c2, other)),
sum(map(_mul, c3, other))) sum(map(_mul, c3, other)))
if not isinstance(other, Mat4):
raise TypeError("Can only multiply with Mat4 or Vec4 types")
# Rows: # Rows:
r0 = self[0:4] r0 = self[0:4]
r1 = self[4:8] r1 = self[4:8]

View File

@ -123,7 +123,7 @@ class AudioData:
length (int): Size of sample data, in bytes. length (int): Size of sample data, in bytes.
timestamp (float): Time of the first sample, in seconds. timestamp (float): Time of the first sample, in seconds.
duration (float): Total data duration, in seconds. duration (float): Total data duration, in seconds.
events (List[:class:`pyglet.media.events.MediaEvent`]): List of events events (List[:class:`pyglet.media.drivers.base.MediaEvent`]): List of events
contained within this packet. Events are timestamped relative to contained within this packet. Events are timestamped relative to
this audio packet. this audio packet.
""" """

File diff suppressed because it is too large Load Diff

View File

@ -356,6 +356,7 @@ class Caret:
m2 = len(self._layout.document.text) m2 = len(self._layout.document.text)
else: else:
m2 = m2.start() m2 = m2.start()
self._position = m2 self._position = m2
self._update(line=line) self._update(line=line)
self._next_attributes.clear() self._next_attributes.clear()
@ -436,6 +437,7 @@ class Caret:
elif self._position > 0: elif self._position > 0:
self._position -= 1 self._position -= 1
self._layout.document.delete_text(self._position, self._position + 1) self._layout.document.delete_text(self._position, self._position + 1)
self._update()
elif motion == key.MOTION_DELETE: elif motion == key.MOTION_DELETE:
if self.mark is not None: if self.mark is not None:
self._delete_selection() self._delete_selection()

View File

@ -347,7 +347,7 @@ class _GlyphBox(_AbstractBox):
v2 += x1 v2 += x1
v1 += y + baseline v1 += y + baseline
v3 += y + baseline v3 += y + baseline
vertices.extend(map(int, [v0, v1, v2, v1, v2, v3, v0, v3])) vertices.extend(map(round, [v0, v1, v2, v1, v2, v3, v0, v3]))
t = glyph.tex_coords t = glyph.tex_coords
tex_coords.extend(t) tex_coords.extend(t)
x1 += glyph.advance x1 += glyph.advance
@ -412,17 +412,19 @@ class _GlyphBox(_AbstractBox):
if background_vertices: if background_vertices:
background_indices = [] background_indices = []
bg_count = len(background_vertices) // 2 bg_count = len(background_vertices) // 2
for glyph_idx in range(bg_count): decoration_program = get_default_decoration_shader()
background_indices.extend([element + (glyph_idx * 4) for element in [0, 1, 2, 0, 2, 3]]) for bg_idx in range(bg_count):
background_indices.extend([element + (bg_idx * 4) for element in [0, 1, 2, 0, 2, 3]])
background_list = program.vertex_list_indexed(bg_count, GL_TRIANGLES, background_indices, background_list = decoration_program.vertex_list_indexed(bg_count * 4, GL_TRIANGLES, background_indices,
layout.batch, layout.background_decoration_group, layout.batch, layout.background_decoration_group,
position=('f', background_vertices), position=('f', background_vertices),
colors=('Bn', background_colors)) colors=('Bn', background_colors))
context.add_list(background_list) context.add_list(background_list)
if underline_vertices: if underline_vertices:
underline_list = program.vertex_list(len(underline_vertices) // 2, GL_LINES, decoration_program = get_default_decoration_shader()
underline_list = decoration_program.vertex_list(len(underline_vertices) // 2, GL_LINES,
layout.batch, layout.foreground_decoration_group, layout.batch, layout.foreground_decoration_group,
position=('f',underline_vertices), position=('f',underline_vertices),
colors=('Bn', underline_colors)) colors=('Bn', underline_colors))
@ -778,6 +780,14 @@ class ScrollableTextDecorationGroup(graphics.Group):
glDisable(GL_BLEND) glDisable(GL_BLEND)
self.program.stop() self.program.stop()
def __repr__(self):
return f"{self.__class__.__name__}(scissor={self.scissor_area})"
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class IncrementalTextDecorationGroup(ScrollableTextDecorationGroup): class IncrementalTextDecorationGroup(ScrollableTextDecorationGroup):
# Subclass so that the scissor_area isn't shared with the # Subclass so that the scissor_area isn't shared with the
@ -1797,6 +1807,9 @@ class ScrollableTextLayout(TextLayout):
for group in self.group_cache.values(): for group in self.group_cache.values():
group.scissor_area = area group.scissor_area = area
self.background_decoration_group.scissor_area = area
self.foreground_decoration_group.scissor_area = area
def _update(self): def _update(self):
super()._update() super()._update()
self._update_scissor_area() self._update_scissor_area()
@ -1951,6 +1964,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
area = self._get_left(), self._get_bottom(self._get_lines()), self._width, self._height area = self._get_left(), self._get_bottom(self._get_lines()), self._width, self._height
for group in self.group_cache.values(): for group in self.group_cache.values():
group.scissor_area = area group.scissor_area = area
self.background_decoration_group.scissor_area = area
self.foreground_decoration_group.scissor_area = area
def _init_document(self): def _init_document(self):
assert self._document, 'Cannot remove document from IncrementalTextLayout' assert self._document, 'Cannot remove document from IncrementalTextLayout'
@ -2532,7 +2547,7 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
position -= box.length position -= box.length
x += box.advance x += box.advance
return x + self._translate_x, line.y + self._translate_y + baseline return x - self._translate_x, line.y + self._translate_y + baseline
def get_line_from_point(self, x, y): def get_line_from_point(self, x, y):
"""Get the closest line index to a point. """Get the closest line index to a point.
@ -2610,6 +2625,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
:rtype: int :rtype: int
""" """
line = self.lines[line] line = self.lines[line]
x += self._translate_x
x -= self._x x -= self._x
if x < line.x: if x < line.x:
@ -2659,6 +2676,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
X coordinate X coordinate
""" """
x += self.view_x - self._x
if x <= self.view_x + 10: if x <= self.view_x + 10:
self.view_x = x - 10 self.view_x = x - 10
elif x >= self.view_x + self.width: elif x >= self.view_x + self.width:

View File

@ -321,6 +321,7 @@ class Win32Window(BaseWindow):
return return
_user32.DestroyWindow(self._hwnd) _user32.DestroyWindow(self._hwnd)
_user32.UnregisterClassW(self._view_window_class.lpszClassName, 0)
_user32.UnregisterClassW(self._window_class.lpszClassName, 0) _user32.UnregisterClassW(self._window_class.lpszClassName, 0)
self._window_class = None self._window_class = None