Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:02

0001 #!/usr/bin/env python
0002 """
0003 cxt_min.py
0004 ===========
0005 
0006 1. loads simtrace SEvt from $MFOLD
0007 2. checks validity with asserts on the simtrace gensteps
0008 3. prepare genstep ugrid positions and upos simtrace intersect positions
0009    depending on GLOBAL envvar
0010 4. plot the ugrid and upos points
0011 
0012 
0013 MODE=2
0014    use matplotlib 2D plotting, little used now : as lacks flexibility and performance
0015 
0016 MODE=3
0017    use pyvista 3D plotting.
0018    Have previously observed pyvista issue that sometimes start with a blank screen.
0019    It is necessary to slighty move the mouse wheel to get the render to appear.
0020 
0021 GLOBAL=1
0022    leave positions as is resulting in global frame plotting
0023 
0024    With global frame the plane of the genstep rays will often not correspond
0025    to the plane of the view causing the geometry to appear squeezed until
0026    rotate around with pyvista (MODE:3) manually.
0027    For the same reason it is difficult to target using EYE
0028    when using GLOBAL frame.
0029 
0030    TODO: provide option to handle EYE,LOOK,UP inputs as local frame
0031    even when using GLOBAL frame ?
0032 
0033 GLOBAL=0
0034    apply the e.f.sframe.w2m (world2model) 4x4 transform to
0035    the global positions converting them into the model frame
0036 
0037 
0038 GSGRID=1
0039    plot the grid of genstep positions
0040 
0041 PRIMTAB=1
0042    configure identity coloring and key labelling of intersects to use globalPrimIdx
0043    (this succeeded to color both Opticks and Geant4 intersects)
0044 
0045 PRIMTAB=0 (default)
0046    configure identity coloring and key labelling of intersects to use boundary index
0047    (note that when plotting U4Simtrace.h Geant4 simtrace intersects the boundary is
0048    currently always zero)
0049 
0050 
0051 KEY=red,white,powderblue
0052    select intersects included in the plot according to their key color
0053 
0054 KEY=~red,white,powderblue
0055    select intersects excluded from the plot according to their key color
0056 
0057 
0058 MODE=3 EYE=0,10000,0
0059    EYE controls 3D viewpoint in pyvista plotting using mm length units,
0060    NB unlike cxr_min.sh there is currently no target extent scaling so
0061    the EYE,LOOK,UP values need to be chosen according to the size
0062    of the target geometry
0063 
0064 
0065 TODO: cherry pick from tests/CSGOptiXSimtraceTest.py and simplify
0066 for minimal simtrace plotting
0067 
0068 
0069 
0070 
0071 This python script is also used from ipc InputPhotonCheck, eg::
0072 
0073     ipc env_info ## get into env
0074     MODE=3 PRIMTAB=1 GSGRID=1 EYE=0,1000,0  ipc cxt             ## viewpoint 1m offset in Y from center of target frame
0075     MODE=3 PRIMTAB=1 GSGRID=1 EYE=0,1000,0 GLOBAL=1 ipc cxt
0076 
0077 
0078     MODE=3 PRIMTAB=1 GSGRID=1 EYE=0,1000,0 GLOBAL=0 KEY=red,white,powderblue ipc cxt
0079     MODE=3 PRIMTAB=1 GSGRID=1 EYE=0,1000,0 GLOBAL=0 KEY=~red,white,powderblue ipc cxt
0080 
0081 
0082 """
0083 import os, sys, re, logging, textwrap, numpy as np
0084 log = logging.getLogger(__name__)
0085 
0086 from collections import OrderedDict
0087 from opticks.ana.fold import Fold
0088 from opticks.sysrap.sevt import SEvt
0089 from opticks.ana.p import cf    # load CSGFoundry geometry info
0090 from opticks.CSG.CSGFoundry import KeyNameConfig
0091 
0092 try:
0093     from scipy.spatial import KDTree
0094 except ImportError:
0095     KDTree = None
0096 pass
0097 
0098 
0099 MODE = int(os.environ.get("MODE","2"))
0100 NORMAL = int(os.environ.get("NORMAL","0"))
0101 NORMAL_FILTER = int(os.environ.get("NORMAL_FILTER","10000")) # modulo scaledown for normal presentation
0102 PRIMTAB = int(os.environ.get("PRIMTAB","0"))
0103 assert PRIMTAB in [0,1]
0104 
0105 
0106 A = int(os.environ.get("A","0"))
0107 B = int(os.environ.get("B","0"))
0108 
0109 
0110 GLOBAL = 1 == int(os.environ.get("GLOBAL","0"))
0111 GSGRID = 1 == int(os.environ.get("GSGRID","0"))
0112 FRAME = 1 == int(os.environ.get("FRAME","0"))
0113 POINT = np.fromstring(os.environ["POINT"],sep=",").reshape(-1,3) if "POINT" in os.environ else None
0114 POINTSIZE = float(os.environ.get("POINTSIZE",1.))
0115 APOINTSIZE = float(os.environ.get("APOINTSIZE",10.))
0116 BPOINTSIZE = float(os.environ.get("BPOINTSIZE",10.))
0117 
0118 
0119 
0120 if MODE in [2,3]:
0121     import opticks.ana.pvplt as pvp
0122 pass
0123 
0124 
0125 
0126 class UniqueTable(object):
0127     def __init__(self, symbol, ii, names=None ):
0128         u, x, c = np.unique(ii, return_index=True, return_counts=True )
0129         o = np.argsort(c)[::-1] ## descending count order
0130         nn = names[u] if not names is None else None
0131 
0132         _tab = "np.c_[u,c,x,nn][o]" if not nn is None else "np.c_[u,c,x][o]"
0133         tab = eval(_tab)
0134 
0135         self.symbol = symbol
0136         self._tab = _tab
0137         self.tab = tab
0138 
0139         self.u = u[o]
0140         self.x = x[o]
0141         self.c = c[o]
0142         self.n = nn[o] if not nn is None else None
0143 
0144 
0145     def __repr__(self):
0146        lines =  []
0147        lines.append("[%s" % self.symbol)
0148        lines.append(repr(self.tab))
0149        lines.append("]%s" % self.symbol)
0150        return "\n".join(lines)
0151 
0152 
0153 
0154 
0155 if __name__ == '__main__':
0156 
0157     logging.basicConfig(
0158            level=logging.INFO,
0159            format='%(asctime)s - %(message)s',
0160            datefmt='%H:%M:%S'   # .%f
0161     )
0162 
0163     log.info("GLOBAL:%d MODE:%d" % (GLOBAL,MODE))
0164 
0165     prn_config = KeyNameConfig.Parse("$HOME/.opticks/GEOM/cxt_min.ini", "key_prim_regexp")
0166 
0167 
0168     if not "MFOLD" in os.environ:
0169        print("FATAL cxt_min.py simtrace plotting now needs MFOLD envvar, not the former FOLD : as that is too common")
0170     pass
0171     e = SEvt.Load("$MFOLD", symbol="e")
0172     a = SEvt.Load("$AFOLD", symbol="a") if A == 1 else None
0173     b = SEvt.Load("$BFOLD", symbol="b") if B == 1 else None
0174 
0175 
0176     print(repr(e))
0177     if e is None:
0178         fmt = "ABORT script as failed to SEvt.Load from MFOLD[%s] GEOM is [%s]"
0179         print(fmt % (os.environ.get("MFOLD", None), os.environ.get("GEOM", None)))
0180         sys.exit(0)
0181     pass
0182 
0183     label = e.f.base
0184     sf = e.f.sframe
0185     w2m = sf.w2m
0186     m2w = sf.m2w
0187 
0188     gs = e.f.genstep
0189     st = e.f.simtrace
0190 
0191     gs_pos = gs[:,1]
0192     all_one = np.all( gs_pos[:,3] == 1. )    ## W
0193     all_zero = np.all( gs_pos[:,:3]  == 0 )  ## XYZ often all zero
0194     ## the gs_pos is regarded as a local position in the
0195     ## frame defined by the transform in the last 4 quads of the simtrace genstep
0196 
0197     assert all_one  # SHOULD ALWAYS BE 1. [HOMOGENOUS POSITION COORDINATE W :  4th ELEMENT]
0198     #assert all_zero # NOT ZERO WHEN USING CE_OFFSET=CE CE_SCALE=1
0199     gs_tra = gs[:,2:]
0200     assert gs_tra.shape[1:] == (4,4)  ## skip first number of gs dimensions
0201 
0202 
0203     ggrid = np.zeros( (len(gs), 4 ), dtype=np.float32 )
0204     for i in range(len(ggrid)): ggrid[i] = np.dot(gs_pos[i], gs_tra[i])
0205     ## np.matmul/np.tensordot/np.einsum can probably do this without the loop
0206     lgrid = np.dot( ggrid, w2m )
0207     ugrid = ggrid if GLOBAL else lgrid
0208 
0209     st_x = st[:,1,0]
0210     st_y = st[:,1,1]
0211     st_z = st[:,1,2]
0212     st_gp = st[:,2,3].view(np.int32) >> 16
0213     st_bn = st[:,2,3].view(np.int32) & 0xffff
0214     st_id = st[:,3,3].view(np.int32)
0215 
0216     presel = slice(None)                                        ## ALL
0217 
0218     #presel = st_x < -20000                                     ## x < -20m
0219     #presel = np.abs(st_x) < 1000                               ## |x|<1m
0220 
0221     PRESEL = os.environ.get("PRESEL", "")
0222     if PRESEL.startswith("PRIM:"):
0223         spec = PRESEL[len("PRIM:"):]
0224         gps = np.array([], dtype=np.int64)
0225         for elem in spec.split(","):
0226             if str.isdigit(elem):
0227                 gps = np.concatenate(gps, int(elem))
0228             else:
0229                 egp = np.unique(np.where( cf.primname == elem ))
0230                 gps = np.concatenate( (gps, egp) )
0231             pass
0232         pass
0233         gps = np.unique(gps)
0234         presel = np.isin(st_gp, gps )
0235         pass
0236     elif PRESEL == "LPMT":
0237         presel = np.logical_and( st_id > 0, st_id < 30000 )
0238     elif PRESEL == "SPMT":
0239         presel = np.logical_and( st_id >= 30000, st_id < 50000 )
0240     elif PRESEL == "PPMT":
0241         presel = st_id >= 50000   ## pool PMTS
0242     elif PRESEL == "GLOBAL":
0243         presel = st_id == 0   ## non-instanced
0244     else:
0245         presel = slice(None)
0246     pass
0247 
0248     #presel = st_gp == 10
0249     #presel = st_gp == 2919
0250     #presel = st_gp == 2919
0251 
0252 
0253 
0254     ust = st[presel]   ## example ust shape (13812151, 4, 4)
0255     ## for simtrace layout see sysrap/sevent.h
0256 
0257     _ii = ust[:,3,3].view(np.int32)   ## instanceIndex
0258     _gp_bn = ust[:,2,3].view(np.int32)    ## simtrace intersect boundary indices
0259     _gp = _gp_bn >> 16      ## globalPrimIdx
0260     _bn = _gp_bn & 0xffff   ## boundary
0261 
0262     gnrm = ust[:,0].copy()
0263     gpos = ust[:,1].copy()
0264 
0265     gnrm[...,3] = 0.   ## surface normal transforms as vector
0266     gpos[...,3] = 1.   ## intersect position transforms as position
0267 
0268     ## transform from global to local frame
0269     lnrm = np.dot( gnrm, w2m )
0270     lpos = np.dot( gpos, w2m )
0271 
0272     elu_m2w = m2w if GLOBAL else np.eye(4)   ## try to make EYE,LOOK,UP stay local even in GLOBAL
0273     _unrm = gnrm if GLOBAL else lnrm
0274     _upos = gpos if GLOBAL else lpos
0275 
0276     if "BOXSEL" in os.environ:
0277         BOXSEL = np.fromstring(os.environ.get("BOXSEL","-1000,1000,-1000,1000,-1000,1000"),sep=",").reshape(3,2)
0278         boxsel = np.all( (_upos[:,:3] > BOXSEL[:,0]) & (_upos[:,:3] < BOXSEL[:,1]), axis=1)
0279         upos = _upos[boxsel]
0280         unrm = _unrm[boxsel]
0281 
0282         ii = _ii[boxsel]
0283         gp = _gp[boxsel]
0284         bn = _bn[boxsel]
0285     else:
0286         upos = _upos
0287         unrm = _unrm
0288         ii = _ii
0289         gp = _gp
0290         bn = _bn
0291     pass
0292 
0293 
0294 
0295 
0296 
0297     EXPR = list(map(str.strip,textwrap.dedent(r"""
0298     st.shape
0299     ust.shape
0300     np.c_[np.unique(gp,return_counts=True)]
0301     np.c_[np.unique(bn,return_counts=True)]
0302     np.c_[np.unique(ii,return_counts=True)]
0303     """).split("\n")))
0304 
0305     for expr in EXPR:
0306         print(expr)
0307         if expr == "" or expr[0] == "#": continue
0308         print(repr(eval(expr)))
0309     pass
0310 
0311 
0312 
0313 
0314 
0315 
0316 
0317     idtab = UniqueTable("idtab", ii, None)
0318     print(repr(idtab))
0319 
0320     gptab = UniqueTable("gptab", gp, cf.primname)
0321     print(repr(gptab))
0322 
0323     ## would be good to see the globalPrimIdx that correspond to a boundary
0324 
0325     KEY = os.environ.get("KEY", None)
0326     KEY_invert = False
0327     if not KEY is None:
0328         if KEY.startswith("~"):
0329             KEY = KEY[1:]
0330             KEY_invert = True
0331         pass
0332         KK = KEY.split(",")
0333     else:
0334         KK = None
0335     pass
0336 
0337     ## analyse frequencies of occurence of the label integers
0338     cxtb, btab = cf.simtrace_boundary_analysis(bn, KEY)
0339     print(repr(cxtb))
0340     print(repr(btab))
0341 
0342     cxtp, ptab = cf.simtrace_prim_analysis(gp, KEY)
0343     print(repr(cxtp))
0344     print(repr(ptab))
0345 
0346     cxtable = cxtp if PRIMTAB==1 else cxtb
0347 
0348 
0349 
0350     ASLICE = None
0351     AIDX = int(os.environ.get("AIDX","0"))
0352     if not a is None:
0353         if "AFOLD_RECORD_SLICE" in os.environ:
0354             ASLICE = os.environ["AFOLD_RECORD_SLICE"]
0355             ase = a.q_startswith(ASLICE)
0356             g_apos = a.f.record[ase][:,:,0].copy()
0357         else:
0358             ase = np.where( a.f.record[:,:,3,3].view(np.int32) > 0 )   ## flagmask selection to skip ufilled step point records
0359             g_apos = a.f.record[ase][:,0].copy()
0360             ## HMM: how to avoid loosing separation between photon records
0361         pass
0362 
0363         g_apos[...,3] = 1.  # transform as position
0364         l_apos = np.dot( g_apos, w2m )
0365         u_apos = g_apos if GLOBAL else l_apos
0366     else:
0367         g_apos = None
0368         l_apos = None
0369         u_apos = None
0370     pass
0371 
0372     if not b is None:
0373         if "BFOLD_RECORD_SLICE" in os.environ:
0374             bse = b.q_startswith(os.environ["BFOLD_RECORD_SLICE"])
0375             g_bpos = b.f.record[bse][:,:,0].copy()
0376         else:
0377             bse = np.where( b.f.record[:,:,3,3].view(np.int32) > 0 )   ## flagmask selection to skip ufilled step point records
0378             g_bpos = b.f.record[bse][:,0].copy()
0379         pass
0380         g_bpos[...,3] = 1.  # transform as position
0381         l_bpos = np.dot( g_bpos, w2m )
0382         u_bpos = g_bpos if GLOBAL else l_bpos
0383     else:
0384         g_bpos = None
0385         l_bpos = None
0386         u_bpos = None
0387     pass
0388 
0389 
0390 
0391     X,Y,Z = 0,1,2
0392     H,V = X,Z
0393 
0394 
0395 
0396     class SimtracePlot(object):
0397         """
0398         defined inline as uses::
0399 
0400              KK
0401              upos
0402              unrm
0403              NORMAL_FILTER
0404 
0405         """
0406         def __init__(self, pl, cxtable, hide=False):
0407             """
0408             :param pl: pyvista plotter
0409             :param cxtable: analysis of simtrace integer such as boundary or globalPrimIdx
0410             """
0411             self.cxtable = cxtable
0412             pl.enable_point_picking(callback=self, use_picker=True, show_message=False)
0413             pcloud = {}
0414             pts = {}
0415             nrm = {}
0416             pcinfo = np.zeros( (len(cxtable.wdict),3), dtype=np.int64 )
0417             i = 0
0418             keys = np.array(list(cxtable.wdict.keys()))
0419             #print("SimtracePlot keys:%s" % str(keys))
0420             for k, w in cxtable.wdict.items():
0421                 if not KK is None:
0422                     skip0 = KEY_invert is False and not k in KK
0423                     skip1 = KEY_invert is True and k in KK
0424                     if skip0: continue
0425                     if skip1: continue
0426                 pass
0427                 label = self.get_label(k)
0428 
0429                 # example KEYOFF "honeydew:0,0,-1000"
0430                 if os.environ.get("KEYOFF","").startswith(k+":"):
0431                     KEYOFF = np.fromstring(os.environ["KEYOFF"][len(k+":"):],sep=",").reshape(3)
0432                 else:
0433                     KEYOFF = np.array([0,0,0], dtype=np.float32)
0434                 pass
0435                 pts[k] = upos[w,:3] + KEYOFF
0436                 nrm[k] = unrm[w,:3]
0437                 if not hide:
0438                     pcloud[k] = pvp.pvplt_add_points(pl, pts[k], color=k, label=label )
0439                 else:
0440                     pcloud[k] = None
0441                 pass
0442                 n_verts = pcloud[k].n_verts if not pcloud[k] is None else 0
0443                 pcinfo[i,0] = n_verts
0444                 if NORMAL > 0: pvp.pvplt_add_delta_lines(pl, upos[w,:3][::NORMAL_FILTER], 20*unrm[w,:3][::NORMAL_FILTER], color=k, pickable=False )
0445                 i += 1
0446             pass
0447             pcinfo[:,1] = np.cumsum(pcinfo[:,0])
0448             pcinfo[:,2] = pcinfo[:,0] + pcinfo[:,1] - 1
0449             self.pcloud = pcloud
0450             self.pcinfo = pcinfo
0451             self.keys = keys
0452             self.pts = pts
0453             self.nrm = nrm
0454 
0455         def get_label(self, k):
0456             cxtable = self.cxtable
0457             w = cxtable.wdict[k]
0458             label = "%15s %8d %s" % (k,len(w),cxtable.d_anno[k])
0459             return label
0460 
0461         def __call__(self, picked_point, picker):
0462             """
0463             """
0464             self.picked_point = picked_point
0465             self.picker = picker
0466 
0467             pcloud = self.pcloud
0468             pcinfo = self.pcinfo
0469 
0470             dataSet = picker.GetDataSet()
0471             pointId = picker.GetPointId()
0472 
0473             idx= list(pcloud.values()).index(dataSet)
0474             k = list(pcloud.keys())[idx]
0475             label = self.get_label(k)
0476 
0477             print("SimtracePlot.__call__ picked_point %s pointId %d idx %d k %s label %s " % (str(picked_point), pointId, idx, k, label) )
0478         pass
0479     pass
0480 
0481 
0482 
0483 
0484 
0485     if MODE == 2:
0486         pl = pvp.mpplt_plotter(label=label)
0487         fig, axs = pl
0488         ax = axs[0]
0489 
0490         for k, w in cxtb.wdict.items():
0491             if not KEY is None and not k in KK: continue
0492             ax.scatter( upos[w,H], upos[w,V], s=0.1, color=k )
0493         pass
0494 
0495         if GSGRID: ax.scatter( ugrid[:,H], ugrid[:,V], s=0.1, color="r" )
0496 
0497         #ax.set_ylim(-9000,2000)
0498         #ax.set_xlim(-22000, 22000)
0499 
0500         fig.show()
0501     elif MODE == 3:
0502         pl = pvp.pvplt_plotter(label)
0503 
0504         pvp.pvplt_viewpoint(pl, verbose=True, m2w=elu_m2w)   # sensitive to EYE, LOOK, UP envvars
0505 
0506 
0507         if FRAME: pvp.pvplt_frame(pl, sf, local=not GLOBAL, pickable=False )
0508 
0509 
0510         HIDE="HIDE" in os.environ
0511         spl = SimtracePlot(pl, cxtable, hide=HIDE)
0512 
0513 
0514         if "OVERLAP" in os.environ and len(spl.pcloud) == 2 and not KDTree is None:
0515             overlap_tol = float(os.environ["OVERLAP"]) # eg 1 0.1 0.01 0.001 or 1e-3
0516             assert overlap_tol > 0
0517             close_tol = overlap_tol*10
0518             print("OVERLAP check overlap_tol : %s close_tol %s " % (overlap_tol, close_tol  ))
0519             k0, k1 = list(spl.pts.keys())
0520             pt0 = spl.pts[k0]    # eg (547840, 3)
0521             pt1 = spl.pts[k1]    # eg (259548, 3)
0522             nr0 = spl.nrm[k0]    # eg (547840, 3)
0523             nr1 = spl.nrm[k1]    # eg (259548, 3)
0524 
0525 
0526             log.info("-OVERLAP-form KDTree kt0 pt0.shape %s" % (str(pt0.shape)))
0527             kt0 = KDTree(pt0)
0528             log.info("-OVERLAP-form KDTree kt1 pt1.shape %s" % (str(pt1.shape)))
0529             kt1 = KDTree(pt1)
0530 
0531             # query nearest neighbors between two clouds
0532             log.info("-OVERLAP-kt0.query pt1.shape %s " % (str(pt1.shape)))
0533             dist_1to0, idx_1to0 = kt0.query(pt1, k=1) # pt1: (259548, 3) dist_1to0: (259548,)  idx_1to0:(259548,)
0534             log.info("-OVERLAP-kt0.query.DONE")
0535             log.info("-OVERLAP-kt1.query pt0.shape %s " % (str(pt0.shape)))
0536             dist_0to1, idx_0to1 = kt1.query(pt0, k=1) #                  dist_0to1: (547840,)
0537             log.info("-OVERLAP-kt1.query.DONE")
0538 
0539             close_0 = dist_0to1 < close_tol
0540             close_1 = dist_1to0 < close_tol
0541 
0542             close_pt0 = pt0[close_0]
0543             close_pt1 = pt1[close_1]
0544             close_pt = np.unique(np.vstack([close_pt0, close_pt1]), axis=0)
0545 
0546             all_sd0 = np.sum( (pt0 - pt1[idx_0to1])*nr1[idx_0to1], axis=1 )
0547             all_sd1 = np.sum( (pt1 - pt0[idx_1to0])*nr0[idx_1to0], axis=1 )
0548 
0549             close_sd0 = np.sum( (pt0 - pt1[idx_0to1])[close_0]*nr1[idx_0to1][close_0], axis=1 )
0550             close_sd1 = np.sum( (pt1 - pt0[idx_1to0])[close_1]*nr0[idx_1to0][close_1], axis=1 )
0551 
0552 
0553             #overlap_pt0 = pt0[all_sd0 < -overlap_tol]
0554             #overlap_pt1 = pt1[all_sd1 < -overlap_tol]
0555             overlap_pt0 = pt0[close_0][close_sd0 < -overlap_tol]
0556             overlap_pt1 = pt1[close_1][close_sd1 < -overlap_tol]
0557 
0558 
0559             overlap_pt = np.unique(np.vstack([overlap_pt0, overlap_pt1]), axis=0)
0560 
0561             cegs_npy = os.environ.get("CEGS_NPY","")  # eg /tmp/overlap_pt.npy see SFrameGenstep::MakeCenterExtentGenstep_FromFrame
0562             if cegs_npy.endswith(".npy"):
0563                 np.save(cegs_npy, overlap_pt)
0564             pass
0565 
0566             close_label = "close_pt  %s " % (len(close_pt))
0567             overlap_label = "overlap_pt  %s " % (len(overlap_pt))
0568 
0569             #pvp.pvplt_add_points(pl, close_pt,   color="yellow", label=close_label,   copy_mesh=True, point_size=5 ) # HUH setting pointsize here effects all points
0570             pvp.pvplt_add_points(pl, overlap_pt, color="yellow", label=overlap_label, copy_mesh=True, point_size=5 ) # HUH setting pointsize here effects all points
0571         else:
0572             close_pd = None
0573         pass
0574 
0575 
0576 
0577         if GSGRID: pl.add_points(ugrid[:,:3], color="r", pickable=False )
0578 
0579         if not u_apos is None: pvp.pvplt_add_points_adaptive(pl, u_apos, color="red",  label="u_apos" ) # point_size=APOINTSIZE )
0580         if not u_bpos is None: pvp.pvplt_add_points_adaptive(pl, u_bpos, color="blue", label="u_bpos" ) # point_size=BPOINTSIZE )
0581 
0582         if not u_apos is None and u_apos.ndim == 3 and not ASLICE is None:
0583             num_step_point = len(ASLICE.split())
0584             pvp.pvplt_add_contiguous_line_segments( pl, u_apos[AIDX,:num_step_point,:3], point_size=4 )
0585         pass
0586 
0587         def callback_click_position(xy):
0588             print("callback_click_position xy : %s " % str(xy))
0589         pass
0590         pl.track_click_position(callback_click_position)
0591         if not POINT is None: pl.add_points(POINT, color="r", label="POINT") # point_size=POINTSIZE, render_points_as_spheres=True)
0592 
0593         #pl.reset_camera() # avoids blank start screen, but resets view to include all points - not what want
0594 
0595         cp = pvp.pvplt_show(pl, incpoi=-5, legend=True, title=None )
0596     else:
0597         pass
0598     pass
0599 pass
0600 
0601