[v0.0.1.1] improve performance by using PolyCollection

This commit is contained in:
sup39 2022-07-19 06:50:56 +09:00
parent ddde241d2b
commit 15aaecde12
4 changed files with 53 additions and 27 deletions

View file

@ -1,4 +1,9 @@
# CHANGELOG # 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) ## \[v0.0.1] init (2022/06/26)
- WFC - WFC
- plot WFC hitbox (x-z plot and x/z-y plot) with matplotlib toolbar - plot WFC hitbox (x-z plot and x/z-y plot) with matplotlib toolbar

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = supSMSTAS name = supSMSTAS
version = 0.0.1 version = 0.0.1.1
author = sup39 author = sup39
author_email = sms@sup39.dev author_email = sms@sup39.dev
description = A tool to support Super Mario Sunshine Tool Assisted Speedrun/Superplay description = A tool to support Super Mario Sunshine Tool Assisted Speedrun/Superplay
@ -23,9 +23,9 @@ packages = find_namespace:
include_package_data = True include_package_data = True
python_requires = >=3.8 python_requires = >=3.8
install_requires = install_requires =
sup-dolphin-memory-lib ~= 0.1.0 sup-dolphin-memory-lib >= 0.1.1
matplotlib ~= 3.5 matplotlib >= 3.5
PyQt5 ~= 5.15 PyQt5 >= 5.15
[options.packages.find] [options.packages.find]
where = src where = src

View file

@ -108,7 +108,7 @@ class WFCWidget(QWidget):
self.showMario = Qt.Checked self.showMario = Qt.Checked
self.airborne = Qt.Checked self.airborne = Qt.Checked
self.yoshi = 0 self.yoshi = 0
self.fps = 4 self.fps = 8
self.updating = False self.updating = False
self.invertX = 0 self.invertX = 0
self.invertZ = 0 self.invertZ = 0
@ -222,9 +222,9 @@ class WFCWidget(QWidget):
pos = array(pos) pos = array(pos)
x, y, z = pos x, y, z = pos
# get collision data (static collision) # 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 if colInfo is None: return
xLimit, zLimit, xBlockCount, ptrStCLR = colInfo xLimit, zLimit, xBlockCount, ptrStCLR, ptrDyCLR = colInfo
## TBGCheckListRoot[zBlockCount][xBlockCount] ## TBGCheckListRoot[zBlockCount][xBlockCount]
colOff = int((z+zLimit)//1024*xBlockCount + (x+xLimit)//1024)*36 colOff = int((z+zLimit)//1024*xBlockCount + (x+xLimit)//1024)*36
## root->ground(12*2).next(4) ## root->ground(12*2).next(4)
@ -236,12 +236,14 @@ class WFCWidget(QWidget):
t0 = time.time() t0 = time.time()
stGnds, stRoofs, stWalls = [ 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) for j in range(3)
] ]
hitboxs = [ hitboxs = [
# ceiling
([ ([
#(c, array((0, -1, 0))) #(c, array((0, -1, 0)))
(makeRoof(tri, 82 if airborne else 2), array([0, -1, 0])) (makeRoof(tri, 82 if airborne else 2), array([0, -1, 0]))
@ -285,6 +287,7 @@ class WFCWidget(QWidget):
(pnXZ, axesXZ), (pnXZ, axesXZ),
]): ]):
ax.patches.clear() ax.patches.clear()
ax.collections.clear()
make_geo_plot(ax, hitboxs, pos, pn, axes) make_geo_plot(ax, hitboxs, pos, pn, axes)
if self.showMario: if self.showMario:
ax.add_patch(patches.Circle(pos[axes], 25, fc='red')) ax.add_patch(patches.Circle(pos[axes], 25, fc='red'))

View file

@ -4,7 +4,7 @@
import numpy as np import numpy as np
from numpy import array from numpy import array
from .shape import Polyhedron from .shape import Polyhedron
import matplotlib.patches as patches from matplotlib.collections import PolyCollection
class Surface: class Surface:
def __init__(self, surtype, surpara, trntype, unk7, verts, vidxs=None, n=None, c=None): 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): def make_geo_plot(ax, hitboxs, p0, pn, axes):
# paras # paras
arrowWidthBase = 60 arrowWidthBase = 70
arrowWidthMul = 0.2 arrowWidthMul = 0.5
arrowLenMax = 80 arrowLenMax = 200 #80
arrowLenTher = 200 arrowLenTher = 200
arrowLenMul1 = 0.7 arrowLenMul1 = 0.7
arrowLenMul2 = 0.35 arrowLenMul2 = 0.35
arrowHeadLenMul = 0.3 arrowHeadLenMul = 0.3
arrowLenMul2off = 0.025 arrowLenMul2off = 0.1 #0.025
arrowCountMax = 20 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 # plot
patches = []
for polys, awmul, alen, facecolor, arcolor in hitboxs: for polys, awmul, alen, facecolor, arcolor in hitboxs:
# draw wall hitboxs (draw in reverse order) # draw wall hitboxs (draw in reverse order)
for poly, n in polys[::-1]: 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] verts = poly.slicePlane(p0, pn)[:,axes]
if len(verts) == 0: continue if len(verts) == 0: continue
# plot hitbox area # 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 # plot arrow
n = n*(1, 0, 1) # no y arrow n = n*(1, 0, 1) # no y arrow
n = n[axes] n = n[axes]
@ -147,27 +158,34 @@ def make_geo_plot(ax, hitboxs, p0, pn, axes):
ncts = nbss.mean(axis=0) ncts = nbss.mean(axis=0)
nrgs = np.abs(np.diff(nbss, axis=0)[0]) nrgs = np.abs(np.diff(nbss, axis=0)[0])
# add arrow # add arrow
maty = array([-n[1], n[0]])*arrowWidth
for p, nrg in zip( for p, nrg in zip(
C+np.matmul(np.column_stack([ncts, ls]), B), C+np.matmul(np.column_stack([ncts, ls]), B),
nrgs, nrgs,
): ):
if nrg > arrowLenTher: if nrg > arrowLenTher:
nrg = min(arrowLenMax, nrg*arrowLenMul2) nrg = min(arrowLenMax, nrg*arrowLenMul2)
dn = n * nrg
offs = [-arrowLenMul2off, 1+arrowLenMul2off] offs = [-arrowLenMul2off, 1+arrowLenMul2off]
else: else:
nrg = min(arrowLenMax, nrg*arrowLenMul1) nrg = min(arrowLenMax, nrg*arrowLenMul1)
dn = n * nrg
offs = [0.5] offs = [0.5]
arrowHeadLen = nrg*arrowHeadLenMul dn = n * nrg
# add patches
verts0 = np.matmul(arrowVerts, [n*nrg, maty])
for off in offs: for off in offs:
ax.arrow( patches.append((
*(p-dn*off), *dn, p-dn*off+verts0,
width=arrowWidth, #min(arrowWidth, nrg*0.15), arcolor, # fc
head_length=arrowHeadLen, arcolor, # ec
length_includes_head=True, ))
color=arcolor,
) 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_xlabel('xyz'[axes[0]])
ax.set_ylabel('xyz'[axes[1]]) ax.set_ylabel('xyz'[axes[1]])