From 76f34a7d0a672f810524e75f225f5457468ca0a6 Mon Sep 17 00:00:00 2001 From: Steve Jackson Date: Fri, 28 Jun 2019 14:55:56 -0700 Subject: [PATCH] Display in registered side-by-side views --- src/athena/bildparser.py | 10 ++++++++++ src/athena/decorations.py | 27 +++++++++++++++++++++----- src/athena/geom.py | 36 ++++++++++++++++++++++++++++++++-- src/athena/viewer.py | 41 +++++++++++++++++++++++---------------- 4 files changed, 90 insertions(+), 24 deletions(-) diff --git a/src/athena/bildparser.py b/src/athena/bildparser.py index c2afc97..04f24f7 100644 --- a/src/athena/bildparser.py +++ b/src/athena/bildparser.py @@ -69,6 +69,16 @@ def conesFromArrows( self ): base = end + axis yield Cone( arrow.color, base.x(), base.y(), base.z(), end.x(), end.y(), end.z(), arrow.r2 ) + def allVertices( self ): + for s in self.spheres: + yield (s.x, s.y, s.z) + for c in self.cylinders: + yield (c.x1, c.y1, c.z1) + yield (c.x2, c.y2, c.z2) + for c in self.arrows: + yield (c.x1, c.y1, c.z1) + yield (c.x2, c.y2, c.z2) + def parseBildFile( filename, scale_factor ): results = OutputDecorations(scale_factor) diff --git a/src/athena/decorations.py b/src/athena/decorations.py index 80552d6..ec3c2f1 100644 --- a/src/athena/decorations.py +++ b/src/athena/decorations.py @@ -11,7 +11,7 @@ class SphereDecorations(Qt3DCore.QEntity): - def __init__(self, parent, bildfile): + def __init__(self, parent, bildfile, transform=None): super().__init__(parent) spherelist = bildfile.spheres num_spheres = len(spherelist) @@ -31,6 +31,12 @@ def __init__(self, parent, bildfile): if color is None: color = QColor('white') vertex_nparr[idx,:] = x, y, z, r, color.redF(), color.greenF(), color.blueF() + if( transform ): + vertex_nparr[:,0:3] = transform(vertex_nparr[:,0:3]) + # Transform radii by the equivalent scaling factor (assume it's equal in all dimensions) + scale = transform(np.ones((1,3)))[0,0] + vertex_nparr[:,3] *= scale + self.geometry = Qt3DRender.QGeometry(self) position_attrname = Qt3DRender.QAttribute.defaultPositionAttributeName() @@ -58,7 +64,7 @@ def __init__(self, parent, bildfile): class CylinderDecorations(Qt3DCore.QEntity): - def __init__(self, parent, bildfile): + def __init__(self, parent, bildfile, transform=None): super().__init__(parent) # Draw the arrow bodies as cylinders too cylinderlist = bildfile.cylinders + list( bildfile.cylindersFromArrows() ) @@ -78,7 +84,13 @@ def __init__(self, parent, bildfile): if color is None: color = QColor('white') vertex_nparr[2*idx,:] = x1, y1, z1, r, color.redF(), color.greenF(), color.blueF() vertex_nparr[2*idx+1,:] = x2, y2, z2, r, color.redF(), color.greenF(), color.blueF() - + + if( transform ): + vertex_nparr[:,0:3] = transform(vertex_nparr[:,0:3]) + # Transform radii by the equivalent scaling factor (assume it's equal in all dimensions) + scale = transform(np.ones((1,3)))[0,0] + vertex_nparr[:,3] *= scale + self.geometry = Qt3DRender.QGeometry(self) position_attrname = Qt3DRender.QAttribute.defaultPositionAttributeName() @@ -106,7 +118,7 @@ def __init__(self, parent, bildfile): class ConeDecorations(Qt3DCore.QEntity): - def __init__(self, parent, bildfile): + def __init__(self, parent, bildfile, transform=None): super().__init__(parent) conelist = list( bildfile.conesFromArrows() ) num_cones = len(conelist) @@ -125,7 +137,12 @@ def __init__(self, parent, bildfile): if color is None: color = QColor('white') vertex_nparr[2*idx,:] = x1, y1, z1, r, color.redF(), color.greenF(), color.blueF() vertex_nparr[2*idx+1,:] = x2, y2, z2, 0, color.redF(), color.greenF(), color.blueF() - + + if( transform ): + vertex_nparr[:,0:3] = transform(vertex_nparr[:,0:3]) + scale = transform(np.ones((1,3)))[0,0] + vertex_nparr[:,3] *= scale + self.geometry = Qt3DRender.QGeometry(self) position_attrname = Qt3DRender.QAttribute.defaultPositionAttributeName() diff --git a/src/athena/geom.py b/src/athena/geom.py index ce5b186..9c5a254 100644 --- a/src/athena/geom.py +++ b/src/athena/geom.py @@ -153,8 +153,13 @@ def dumpGeometry( geom, dumpf=print ): dumpf(tri) class AABB: def __init__(self, geom): - vertices = getQAttribute( geom, att_name = Qt3DRender.QAttribute.defaultPositionAttributeName() ) - it = iterAttr(vertices) + if hasattr(geom, 'allVertices'): + # Something lke bildparser.OutputDecorations + it = geom.allVertices() + else: + # assume it's a qt3d geometry + vertices = getQAttribute( geom, att_name = Qt3DRender.QAttribute.defaultPositionAttributeName() ) + it = iterAttr(vertices) v0 = next(it) self.min = vec3d( v0[0], v0[1], v0[2]) self.max = vec3d( self.min ) @@ -167,6 +172,33 @@ def __init__(self, geom): self.max.setZ( max( self.max.z(), v[2] ) ) self.center = (self.min+self.max) / 2.0 + def iterCorners(self, cons = vec3d ): + for x in [self.min.x(), self.max.x()]: + for y in [self.min.y(), self.max.y()]: + for z in [self.min.z(), self.max.z()]: + yield cons(x, y, z) + +def transformBetween( aabb1, aabb2 ): + + def np_coords_from_aabb(aabb): + return np.array( list ( aabb.iterCorners(cons=lambda *x:np.array([*x])) ) ) + + coord_from = np_coords_from_aabb( aabb1 ) + coord_to = np_coords_from_aabb( aabb2 ) + + n = coord_from.shape[0] + pad = lambda x: np.hstack([x, np.ones((x.shape[0],1))]) + unpad = lambda x: x[:,:-1] + + X = pad(coord_from) + Y = pad(coord_to) + + A, res, rank, s = np.linalg.lstsq(X, Y, rcond=None) + + transform = lambda x: unpad(np.dot(pad(x), A)) + + return transform + diff --git a/src/athena/viewer.py b/src/athena/viewer.py index d23790c..3d5739e 100644 --- a/src/athena/viewer.py +++ b/src/athena/viewer.py @@ -1,5 +1,6 @@ from pathlib import Path import math +import numpy as np from PySide2.QtGui import QColor, QVector3D as vec3d from PySide2.QtCore import QUrl, QByteArray, Qt, Signal, QRectF @@ -22,7 +23,7 @@ def __init__(self, window, camera, geometry): self.geometry = geometry def _windowAspectRatio(self): - return self.window.width() / self.window.height() + return self.window.width() / (2 * self.window.height()) def reset(self): pass @@ -135,7 +136,7 @@ def clamp(min_, max_, value): self.camera.setFieldOfView( new_fov ) def resize(self, new_width, new_height): - new_ratio = new_width / new_height + new_ratio = new_width / (2 * new_height) self.camera.setAspectRatio(new_ratio) class _metaParameters(type(Qt3DExtras.Qt3DWindow)): @@ -312,9 +313,7 @@ def __init__(self): # Dear qt3d: this absolutely should not have been this hard. self.surfaceSelector = Qt3DRender.QRenderSurfaceSelector() self.surfaceSelector.setSurface(self) - self.viewport = Qt3DRender.QViewport(self.surfaceSelector) - self.viewport.setNormalizedRect(QRectF(0, 0, 1.0, 1.0)) - self.cameraSelector = Qt3DRender.QCameraSelector(self.viewport) + self.cameraSelector = Qt3DRender.QCameraSelector(self.surfaceSelector) self.cameraSelector.setCamera(self.camera()) self.clearBuffers = Qt3DRender.QClearBuffers(self.cameraSelector) self.clearBuffers.setBuffers(Qt3DRender.QClearBuffers.ColorDepthBuffer) @@ -325,23 +324,26 @@ def __init__(self): self.solidPassFilter.setName('pass') self.solidPassFilter.setValue('solid') self.qfilt.addMatch(self.solidPassFilter) + self.viewport = Qt3DRender.QViewport(self.qfilt) + self.viewport.setNormalizedRect(QRectF(.5, 0, 0.5, 1.0)) self.qfilt2 = Qt3DRender.QTechniqueFilter(self.cameraSelector) self.transPassFilter = Qt3DRender.QFilterKey(self.qfilt2) self.transPassFilter.setName('pass') self.transPassFilter.setValue('transp') self.qfilt2.addMatch(self.transPassFilter) + self.viewport2 = Qt3DRender.QViewport(self.qfilt2) + self.viewport2.setNormalizedRect(QRectF(0, 0, .5, 1.0)) - # Framegraph display and testing code - #fg = self.activeFrameGraph() - #def frameGraphLeaf(node, prefix=' '): - #print(prefix, node, node.objectName()) - #children = node.children() - #for c in children: - #frameGraphLeaf(c, prefix+'-') - - #frameGraphLeaf(fg) self.setActiveFrameGraph(self.surfaceSelector) + + # Framegraph display and testing code + def frameGraphLeaf(node, prefix=' '): + print(prefix, node, node.objectName()) + children = node.children() + for c in children: + frameGraphLeaf(c, prefix+'-') + #frameGraphLeaf(self.activeFrameGraph()) self.setBackgroundColor( QColor(63,63,63) ) @@ -494,16 +496,21 @@ def resizeEvent( self, event ): def newDecoration(self, parent, bild_results): + decoration_aabb = geom.AABB( bild_results ) + geom_aabb = self.camControl.aabb + + T = geom.transformBetween( decoration_aabb, geom_aabb ) + if( bild_results.spheres ): - parent.spheres = decorations.SphereDecorations(parent, bild_results) + parent.spheres = decorations.SphereDecorations(parent, bild_results, T) parent.spheres.addComponent( self.sphere_material ) if( bild_results.cylinders ): - parent.cylinders = decorations.CylinderDecorations(parent, bild_results) + parent.cylinders = decorations.CylinderDecorations(parent, bild_results, T) parent.cylinders.addComponent( self.cylinder_material ) if( bild_results.arrows ): - parent.cones = decorations.ConeDecorations(parent, bild_results) + parent.cones = decorations.ConeDecorations(parent, bild_results, T) parent.cones.addComponent( self.cone_material ) def setCylDisplay(self, bild_results):