Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional 3D Display, useful to get around buggy Mayavi install #118

Open
wants to merge 7 commits into
base: force_cpu
Choose a base branch
from
4 changes: 2 additions & 2 deletions pytissueoptics/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pytissueoptics import __version__
from pytissueoptics.examples import loadExamples

ap = argparse.ArgumentParser(prog="python -m pytissueoptics", description="Run PyTissueOptics examples. ")
ap = argparse.ArgumentParser(prog=f"{sys.executable} -m pytissueoptics", description="Run PyTissueOptics examples. ")
ap.add_argument("-v", "--version", action="version", version=f"PyTissueOptics {__version__}")
ap.add_argument("-e", "--examples", required=False, default="all",
help="Run specific examples by number, e.g. -e 1,2,3. ")
Expand All @@ -26,7 +26,7 @@

if runTests:
moduleDir = os.path.dirname(__file__)
err = os.system(f"python -m unittest discover -s {moduleDir} -p 'test*.py'")
err = os.system(f"{sys.executable} -m unittest discover -s {moduleDir} -p 'test*.py'")
sys.exit(err)

if runExamples == "all":
Expand Down
4 changes: 3 additions & 1 deletion pytissueoptics/rayscattering/display/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .direction import Direction, DEFAULT_X_VIEW_DIRECTIONS, DEFAULT_Y_VIEW_DIRECTIONS, DEFAULT_Z_VIEW_DIRECTIONS
from .volumeSlicer import VolumeSlicer
# from .volumeSlicer import VolumeSlicer

__all__ = ["Direction"]
3 changes: 2 additions & 1 deletion pytissueoptics/rayscattering/display/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import List, Tuple, Union

import numpy as np
import os

from pytissueoptics.rayscattering import utils
from pytissueoptics.rayscattering.energyLogging import EnergyLogger
Expand Down Expand Up @@ -93,7 +94,7 @@ def listViews(self):
def show3D(self, visibility=Visibility.AUTO, viewsVisibility: Union[ViewGroup, List[int]] = ViewGroup.SCENE,
pointCloudStyle=PointCloudStyle(), viewsSolidLabels: List[str] = None, viewsSurfaceLabels: List[str] = None,
viewsLogScale: bool = True, viewsColormap: str = "viridis"):
if not MAYAVI_AVAILABLE:
if not MAYAVI_AVAILABLE or os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1':
utils.warn("Package 'mayavi' is not available. Please install it to use 3D visualizations.")
return

Expand Down
2 changes: 1 addition & 1 deletion pytissueoptics/rayscattering/display/views/view2D.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def getImageDataWithDefaultAlignment(self, logScale: bool = True) -> np.ndarray:
return image

def show(self, logScale: bool = True, colormap: str = 'viridis'):
cmap = copy.copy(matplotlib.cm.get_cmap(colormap))
cmap = copy.copy(matplotlib.colormaps.get_cmap(colormap))
cmap.set_bad(cmap.colors[0])

image = self.getImageData(logScale=logScale)
Expand Down
18 changes: 11 additions & 7 deletions pytissueoptics/rayscattering/scatteringScene.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from typing import List

import numpy as np
import os

from pytissueoptics import Vector
from pytissueoptics.rayscattering.materials import ScatteringMaterial
from pytissueoptics.scene import MayaviViewer, Scene
from pytissueoptics.scene import Scene
from pytissueoptics.scene.solids import Solid
from pytissueoptics.scene.viewer.displayable import Displayable

from pytissueoptics.scene import MayaviViewer, MAYAVI_AVAILABLE

class ScatteringScene(Scene):
def __init__(self, solids: List[Solid], worldMaterial=ScatteringMaterial(), ignoreIntersections: bool = False):
Expand All @@ -21,11 +22,14 @@ def add(self, solid: Solid, position: Vector = None):
super().add(solid, position)

def show(self, source: Displayable = None, opacity=0.8, colormap="cool", **kwargs):
viewer = MayaviViewer()
self.addToViewer(viewer, opacity=opacity, colormap=colormap, **kwargs)
if source:
source.addToViewer(viewer)
viewer.show()
if MAYAVI_AVAILABLE:
viewer = MayaviViewer()
self.addToViewer(viewer, opacity=opacity, colormap=colormap, **kwargs)
if source:
source.addToViewer(viewer)
viewer.show()
else:
print("3D viewer unavailable")

def getEstimatedIPP(self, weightThreshold: float) -> float:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np
from matplotlib import pyplot as plt

from pytissueoptics import Direction
from pytissueoptics.rayscattering.display.utils import Direction
from pytissueoptics.rayscattering.display.profiles import Profile1D
from pytissueoptics.rayscattering.tests import SHOW_VISUAL_TESTS
from pytissueoptics.scene.tests import compareVisuals
Expand Down
25 changes: 14 additions & 11 deletions pytissueoptics/rayscattering/tests/display/testViewer.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import sys
import os
import unittest
from unittest.mock import patch

import numpy as np
from mockito import mock, when, verify, ANY

from pytissueoptics import Direction, View2DProjectionX, ViewGroup
from pytissueoptics.scene.logger import Logger
from pytissueoptics.scene.geometry import BoundingBox
from pytissueoptics.rayscattering.energyLogging import EnergyLogger
from pytissueoptics.rayscattering.scatteringScene import ScatteringScene
from pytissueoptics.rayscattering.source import Source
from pytissueoptics.rayscattering.display.viewer import Viewer, Visibility, PointCloudStyle
from pytissueoptics.rayscattering.display.profiles import ProfileFactory, Profile1D
from pytissueoptics.rayscattering.display.views import View2D
from pytissueoptics.rayscattering.energyLogging import PointCloudFactory, PointCloud

if os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '0':
from pytissueoptics import Direction, View2DProjectionX, ViewGroup
from pytissueoptics.scene.logger import Logger
from pytissueoptics.scene.geometry import BoundingBox
from pytissueoptics.rayscattering.energyLogging import EnergyLogger
from pytissueoptics.rayscattering.scatteringScene import ScatteringScene
from pytissueoptics.rayscattering.source import Source
from pytissueoptics.rayscattering.display.viewer import Viewer, Visibility, PointCloudStyle
from pytissueoptics.rayscattering.display.profiles import ProfileFactory, Profile1D
from pytissueoptics.rayscattering.display.views import View2D
from pytissueoptics.rayscattering.energyLogging import PointCloudFactory, PointCloud


def patchMayaviRender(func):
for module in ['show', 'gcf', 'figure', 'clf', 'triangular_mesh', 'points3d']:
func = patch('mayavi.mlab.' + module)(func)
return func


@unittest.skipIf(os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1','No display available')
class TestViewer(unittest.TestCase):
def setUp(self):
self.scene = mock(ScatteringScene)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def _getReflectionCoefficient(self, rayDirection: Vector) -> float:
self.program.launchKernel("getReflectionCoefficientKernel", N=N,
arguments=[np.float32(self.n1), np.float32(self.n2),
np.float32(thetaIn), coefficientBuffer])
return float(self.program.getData(coefficientBuffer)[0])
return float(self.program.getData(coefficientBuffer)[0][0])

def _getFresnelResult(self, fresnelBuffer) -> FresnelResult:
fresnelIntersection = self.program.getData(fresnelBuffer)[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ def test_call_random_value_with_same_seeds_buffer_should_give_same_results(self)
program.launchKernel(kernelName='fillRandomFloatBuffer', N=nWorkUnits, arguments = [seeds2, valueBuffer2])

# We need to sort because values are not necessarily in order
print(sorted(valueBuffer1.hostBuffer))
print(sorted(valueBuffer2.hostBuffer))

for value1, value2 in zip(sorted(valueBuffer1.hostBuffer), sorted(valueBuffer2.hostBuffer)) :
self.assertEqual(value1, value2)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def test001ScalarMultiplicationOfOpenCLArrays(self):
startTime = time.time()
b = a+a
calcTime = (time.time()-startTime)*1000
print("\nOpenCL 1 scalar: {0:.1f} ms ".format(calcTime))
# print("\nOpenCL 1 scalar: {0:.1f} ms ".format(calcTime))

a = np.array(object=[0]*(2<<14), dtype=pycl.cltypes.float)
for i in range(a.size):
Expand All @@ -209,7 +209,7 @@ def test001ScalarMultiplicationOfOpenCLArrays(self):
startTime = time.time()
b = a+a
calcTime = (time.time()-startTime)*1000
print("\nnumpy: {0:.1f} ms ".format(calcTime))
# print("\nnumpy: {0:.1f} ms ".format(calcTime))

a = clArray(cq=queue, shape=(2<<14,), dtype=pycl.cltypes.float)
for i in range(a.size):
Expand All @@ -218,7 +218,7 @@ def test001ScalarMultiplicationOfOpenCLArrays(self):
startTime = time.time()
b = a+a
calcTime = (time.time()-startTime)*1000
print("\nOpenCL 2 scalar: {0:.1f} ms ".format(calcTime))
# print("\nOpenCL 2 scalar: {0:.1f} ms ".format(calcTime))

@unittest.skip("Skipping for now")
def test002ArraysWithAllocator(self):
Expand Down Expand Up @@ -267,7 +267,7 @@ def test002ArraysWithAllocator(self):
calcTimeOpenCL2 = (time.time()-startTime)*1000

self.assertTrue(calcTimeOpenCL2 < calcTimeNumpy,msg="\nNumpy is faster than OpenCL: CL1 {0:.1f} ms NP {1:.1f} ms CL2 {2:.1f} ms".format(calcTimeOpenCL1, calcTimeNumpy, calcTimeOpenCL2))
print("\nCL1 {0:.1f} ms NP {1:.1f} ms".format(calcTimeOpenCL2, calcTimeNumpy))
# print("\nCL1 {0:.1f} ms NP {1:.1f} ms".format(calcTimeOpenCL2, calcTimeNumpy))

@unittest.skip("Skipping graphic tests")
def test003PerformanceVsSize(self):
Expand Down Expand Up @@ -382,7 +382,7 @@ def test004_2x2Matrix_and_Vectors(self):

knl(queue, (N,), None, matrix.data, M, vector.data, result.data)

print("\n{0:0.1f} ms".format((time.time()-startTime)*1000))
# print("\n{0:0.1f} ms".format((time.time()-startTime)*1000))


class TestOpenCLIds(unittest.TestCase):
Expand Down
6 changes: 5 additions & 1 deletion pytissueoptics/rayscattering/tests/testScatteringScene.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import math
import unittest
import os
from unittest.mock import patch

from mockito import mock, verify, when

from pytissueoptics.rayscattering.scatteringScene import ScatteringScene
from pytissueoptics.rayscattering.materials import ScatteringMaterial
from pytissueoptics.scene.solids import Cuboid
from pytissueoptics.scene.viewer import MayaviViewer


def patchMayaviShow(func):
Expand All @@ -27,7 +27,10 @@ def testWhenAddingASolidWithNoScatteringMaterialDefined_shouldRaiseException(sel
with self.assertRaises(Exception):
ScatteringScene([Cuboid(1, 1, 1, material="Not a scattering material")])

@unittest.skipIf(os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1','No display available')
def testWhenAddToViewer_shouldAddAllSolidsToViewer(self):
from pytissueoptics.scene.viewer import MayaviViewer

scene = ScatteringScene([Cuboid(1, 1, 1, material=ScatteringMaterial())])
viewer = mock(MayaviViewer)
when(viewer).add(...).thenReturn()
Expand All @@ -36,6 +39,7 @@ def testWhenAddToViewer_shouldAddAllSolidsToViewer(self):

verify(viewer).add(*scene.solids, ...)

@unittest.skipIf(os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1','No display available')
@patchMayaviShow
def testWhenShow_shouldShowInsideMayaviViewer(self, mockShow, *args):
scene = ScatteringScene([Cuboid(1, 1, 1, material=ScatteringMaterial())])
Expand Down
9 changes: 5 additions & 4 deletions pytissueoptics/scene/tests/viewer/testMayaviViewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

import numpy as np
from matplotlib import pyplot as plt

from pytissueoptics import Logger
from pytissueoptics.scene.solids import Cuboid, Sphere, Ellipsoid
from pytissueoptics.scene.scene import Scene
from pytissueoptics.scene.geometry import Vector
from pytissueoptics.scene.viewer.mayavi import MayaviViewer, ViewPointStyle
from pytissueoptics.scene.tests import SHOW_VISUAL_TESTS, compareVisuals

if os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '0':
from pytissueoptics.scene.viewer.mayavi import MayaviViewer, ViewPointStyle
from pytissueoptics.scene.tests import SHOW_VISUAL_TESTS, compareVisuals

TEST_IMAGES_DIR = os.path.join(os.path.dirname(__file__), 'testImages')

Expand All @@ -23,7 +24,7 @@ def patchMayaviShow(func):
func = patch('mayavi.mlab.' + module)(func)
return func


@unittest.skipIf(os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1','No display available')
class TestMayaviViewer(unittest.TestCase):
def setUp(self):
self.viewer = MayaviViewer()
Expand Down
12 changes: 9 additions & 3 deletions pytissueoptics/scene/viewer/mayavi/MayaviViewer.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import numpy as np

import os
from pytissueoptics.scene.geometry import BoundingBox
from pytissueoptics.scene.logger import Logger
from pytissueoptics.scene.viewer.mayavi.viewPoint import ViewPointStyle, ViewPointFactory


try:
from mayavi import mlab
MAYAVI_AVAILABLE = True
if os.environ.get('PYTISSUE_NO3DDISPLAY','0') == '1':
MAYAVI_AVAILABLE = False
mlab = None
else:
from mayavi import mlab
MAYAVI_AVAILABLE = True

except ImportError:
MAYAVI_AVAILABLE = False
mlab = None

from pytissueoptics.scene.viewer.mayavi import MayaviSolid
from pytissueoptics.scene.solids import Solid
Expand Down