diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c4298d..a8535d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # CHANGELOG +## \[v0.0.1.1] improve performance by using PolyCollection (2022/07/19) +- WFC + - improve process speed of `make_geo_plot()` by using PolyCollection +- README + - add Japanese version of README ## \[v0.0.1] init (2022/06/26) - WFC - plot WFC hitbox (x-z plot and x/z-y plot) with matplotlib toolbar diff --git a/setup.cfg b/setup.cfg index 6053f16..7fcdec7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = supSMSTAS -version = 0.0.1 +version = 0.0.1.1 author = sup39 author_email = sms@sup39.dev description = A tool to support Super Mario Sunshine Tool Assisted Speedrun/Superplay @@ -23,9 +23,9 @@ packages = find_namespace: include_package_data = True python_requires = >=3.8 install_requires = - sup-dolphin-memory-lib ~= 0.1.0 - matplotlib ~= 3.5 - PyQt5 ~= 5.15 + sup-dolphin-memory-lib >= 0.1.1 + matplotlib >= 3.5 + PyQt5 >= 5.15 [options.packages.find] where = src diff --git a/src/supSMSTAS/UI.py b/src/supSMSTAS/UI.py index 054aff5..8aa3115 100644 --- a/src/supSMSTAS/UI.py +++ b/src/supSMSTAS/UI.py @@ -108,7 +108,7 @@ class WFCWidget(QWidget): self.showMario = Qt.Checked self.airborne = Qt.Checked self.yoshi = 0 - self.fps = 4 + self.fps = 8 self.updating = False self.invertX = 0 self.invertZ = 0 @@ -222,9 +222,9 @@ class WFCWidget(QWidget): pos = array(pos) x, y, z = pos # get collision data (static collision) - colInfo = d.read_struct(('gpMap', 0x10, 0), '>ffI4x4xI') + colInfo = d.read_struct(('gpMap', 0x10, 0), '>ffI4x4xII') if colInfo is None: return - xLimit, zLimit, xBlockCount, ptrStCLR = colInfo + xLimit, zLimit, xBlockCount, ptrStCLR, ptrDyCLR = colInfo ## TBGCheckListRoot[zBlockCount][xBlockCount] colOff = int((z+zLimit)//1024*xBlockCount + (x+xLimit)//1024)*36 ## root->ground(12*2).next(4) @@ -236,12 +236,14 @@ class WFCWidget(QWidget): t0 = time.time() stGnds, stRoofs, stWalls = [ - #data1 = [ # TODO dynamic data - d.checkList2list(d.read_uint32(ptrStCLR+colOff+4+12*j)) + [ + tri + for ptrCLR in [ptrStCLR, ptrDyCLR] + for tri in d.checkList2list(d.read_uint32(ptrCLR+colOff+4+12*j)) + ] for j in range(3) ] hitboxs = [ - # ceiling ([ #(c, array((0, -1, 0))) (makeRoof(tri, 82 if airborne else 2), array([0, -1, 0])) @@ -285,6 +287,7 @@ class WFCWidget(QWidget): (pnXZ, axesXZ), ]): ax.patches.clear() + ax.collections.clear() make_geo_plot(ax, hitboxs, pos, pn, axes) if self.showMario: ax.add_patch(patches.Circle(pos[axes], 25, fc='red')) diff --git a/src/supSMSTAS/WFC.py b/src/supSMSTAS/WFC.py index 0ab5187..16f900b 100644 --- a/src/supSMSTAS/WFC.py +++ b/src/supSMSTAS/WFC.py @@ -4,7 +4,7 @@ import numpy as np from numpy import array from .shape import Polyhedron -import matplotlib.patches as patches +from matplotlib.collections import PolyCollection class Surface: def __init__(self, surtype, surpara, trntype, unk7, verts, vidxs=None, n=None, c=None): @@ -81,17 +81,28 @@ def makeWall(tri, rW=50, dy=30): def make_geo_plot(ax, hitboxs, p0, pn, axes): # paras - arrowWidthBase = 60 - arrowWidthMul = 0.2 - arrowLenMax = 80 + arrowWidthBase = 70 + arrowWidthMul = 0.5 + arrowLenMax = 200 #80 arrowLenTher = 200 arrowLenMul1 = 0.7 arrowLenMul2 = 0.35 arrowHeadLenMul = 0.3 - arrowLenMul2off = 0.025 - arrowCountMax = 20 + arrowLenMul2off = 0.1 #0.025 + arrowCountMax = 100 + aw, ah = 0.2, 0.6 # width, height of bottom rect + arrowVerts = array([ + [0.0, aw], + [ ah, aw], + [ ah, 0.5], + [1.0, 0.0], + [ ah, -.5], + [ ah, -aw], + [0.0, -aw], + ]) # plot + patches = [] for polys, awmul, alen, facecolor, arcolor in hitboxs: # draw wall hitboxs (draw in reverse order) for poly, n in polys[::-1]: @@ -99,7 +110,7 @@ def make_geo_plot(ax, hitboxs, p0, pn, axes): verts = poly.slicePlane(p0, pn)[:,axes] if len(verts) == 0: continue # plot hitbox area - ax.add_patch(patches.Polygon(verts, fc=facecolor, ec='black', lw=1)) + patches.append((verts, facecolor, 'black')) # (verts, fc, ec) # plot arrow n = n*(1, 0, 1) # no y arrow n = n[axes] @@ -147,27 +158,34 @@ def make_geo_plot(ax, hitboxs, p0, pn, axes): ncts = nbss.mean(axis=0) nrgs = np.abs(np.diff(nbss, axis=0)[0]) # add arrow + maty = array([-n[1], n[0]])*arrowWidth for p, nrg in zip( C+np.matmul(np.column_stack([ncts, ls]), B), nrgs, ): if nrg > arrowLenTher: nrg = min(arrowLenMax, nrg*arrowLenMul2) - dn = n * nrg offs = [-arrowLenMul2off, 1+arrowLenMul2off] else: nrg = min(arrowLenMax, nrg*arrowLenMul1) - dn = n * nrg offs = [0.5] - arrowHeadLen = nrg*arrowHeadLenMul + dn = n * nrg + # add patches + verts0 = np.matmul(arrowVerts, [n*nrg, maty]) for off in offs: - ax.arrow( - *(p-dn*off), *dn, - width=arrowWidth, #min(arrowWidth, nrg*0.15), - head_length=arrowHeadLen, - length_includes_head=True, - color=arcolor, - ) + patches.append(( + p-dn*off+verts0, + arcolor, # fc + arcolor, # ec + )) + + if len(patches): + ax.add_collection(PolyCollection( + **{ + k: v + for k, v in zip(('verts', 'facecolors', 'edgecolors'), zip(*patches)) + }, + )) ax.set_xlabel('xyz'[axes[0]]) ax.set_ylabel('xyz'[axes[1]])