# This software and supporting documentation are distributed by
# Institut Federatif de Recherche 49
# CEA/NeuroSpin, Batiment 145,
# 91191 Gif-sur-Yvette cedex
# France
#
# This software is governed by the CeCILL-B license under
# French law and abiding by the rules of distribution of free software.
# You can use, modify and/or redistribute the software under the
# terms of the CeCILL-B license as circulated by CEA, CNRS
# and INRIA at the following URL "http://www.cecill.info".
#
# As a counterpart to the access to the source code and rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty and the software's author, the holder of the
# economic rights, and the successive licensors have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading, using, modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also
# therefore means that it is reserved for developers and experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and, more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL-B license and that you accept its terms.
#########################################################################
#
# Project : Pyaimsalgo
# Module : aimsalgo.samplables
# Create date : 2007-07-13
#
# Description :
# This file contains SuperQuadricSamplable class
#
#########################################################################
from __future__ import absolute_import
import sys
import math
import types
import numpy
from soma import aimsalgo, aims
from soma.aimsalgo import Samplable_FLOAT_3
from soma.wip.aimsalgo.transform import BendingTransform
from soma.wip.aimsalgo.transform import TaperingTransform
from six.moves import range
[docs]class SuperQuadricSamplable(Samplable_FLOAT_3):
"""
Constructor of the class.
@type coefficients: list
@param checkActivated: list of the 10 deformable superquadric coefficients.
- e1 : first shape parameter
- e2 : second shape parameter
- a1 : x axis ray
- a2 : y axis ray
- a3 : z axis ray
- k1 : first tapering parameter
- k2 : second tapering parameter
- k3 : third tapering parameter
- alpha : angle of bending plan
- kapa : curvature radius for the bending transform
"""
def __init__(self, coefficients, bending=True, tapering=True, checkActivated=True, maxBoundingBoxVolume=10 ** 7):
Samplable_FLOAT_3.__init__(self)
self._checking = dict()
self._coefficients = coefficients
self._bending = bending
self._bendingTransform = BendingTransform(coefficients[8:10])
self._tapering = tapering
self._taperingTransform = TaperingTransform(coefficients[4:8])
self._maxBoundingBoxVolume = maxBoundingBoxVolume
self._offset = self.getBoundingBoxStart()
self._checked = self.check(checkActivated)
"""
Check that the deformable superquadric parameters are consistents.
@type checkActivated: bool
@param checkActivated: Specify if the check is activated.
"""
def check(self, checkActivated):
e1 = self._coefficients[0]
e2 = self._coefficients[1]
a1 = self._coefficients[2]
a2 = self._coefficients[3]
a3 = self._coefficients[4]
k1 = self._coefficients[5]
k2 = self._coefficients[6]
k3 = self._coefficients[7]
alpha = self._coefficients[8]
kapa = self._coefficients[9]
if (checkActivated):
# Check that parameters verify constraints
# Check for a3 parameter
checked = (a3 != 0)
self._checking['a3'] = checked
result = checked
# Check for e1 parameter
checked = (e1 != 0)
self._checking['e1'] = checked
result &= checked
# Check for e1 parameter
checked = (e2 != 0)
self._checking['e2'] = checked
result &= checked
if (self._tapering):
# Check for k1 parameter
if (result):
checked = (((-1 * k3 * (a3 ** -1)) - 1) <= k1)
checked &= (k1 <= ((k3 * (a3 ** -1)) + 1))
self._checking['k1'] = checked
result = checked
# Check for k2 parameter
if (result):
checked = (((-1 * k3 * (a3 ** -1)) - 1) <= k2)
checked &= (k2 <= ((k3 * (a3 ** -1)) + 1))
self._checking['k2'] = checked
result &= checked
if (self._bending):
# Check for kappa parameter
if (kapa != 0):
kapaInversed = kapa ** -1
checked = ((a3 / math.pi) < math.fabs(kapaInversed))
checked &= ((math.sqrt(a1**2 * ((math.cos(alpha) ** 2) ** e2)
+ a2**2 * ((math.sin(alpha) ** 2) ** e2))) < math.fabs(kapaInversed))
else:
checked = True
self._checking['kapa'] = checked
result &= checked
sizes = self.getBoundingBoxSizes()
if (self._maxBoundingBoxVolume is not None):
# Check for bounding box volume
volume = sizes[0] * sizes[1] * sizes[2]
checked = (volume <= self._maxBoundingBoxVolume)
self._checking['boundingboxvolume'] = checked
result &= checked
# Check that no overflow occurs
try:
self._checked = checked
self.contains(sizes)
except:
checked = False
self._checking['overflow'] = checked
result &= checked
return result
else:
return True
"""
Check that a point is contained in the current deformable superquadric.
This method is called by samplers during sampling to check that the point
is contained by the object.
@type point: bool
@param point: The point to check.
"""
def isChecked(self):
return self._checked
"""
Check that a point is contained in the current deformable superquadric.
This method is called by samplers during sampling to check that the point
is contained by the object.
@type point: aims.Point3df
@param point: The point to check.
"""
def contains(self, point):
if (self._checked):
# The superquadric has always its bounding box start point in (0, 0, 0)
# this allow to always sample BucketMap
point += self._offset
e1 = self._coefficients[0]
e2 = self._coefficients[1]
a1 = self._coefficients[2]
a2 = self._coefficients[3]
a3 = self._coefficients[4]
if (self._bending):
# Apply inverse of the bending transformation
transformed = self._bendingTransform.inverseTransform(point)
else:
transformed = point
if (self._tapering):
transformed = self._taperingTransform.inverseTransform(
transformed)
x = transformed[0]
y = transformed[1]
z = transformed[2]
# Check that point belongs to superquadric object
result = ((math.fabs(x/a1)**(2/e2)) + (math.fabs(y/a2) **
(2/e2)))**(e2/e1) + (math.fabs(z/a3)**(2/e1))
return (result <= 1)
else:
return False
"""
Get bounding box start for the current superquadric.
@rtype: aims.Point3df
@return: start of the bounding box for the superquadric.
"""
def getBoundingBoxStart(self):
a1 = self._coefficients[2]
a2 = self._coefficients[3]
a3 = self._coefficients[4]
alpha = self._coefficients[8]
kapa = self._coefficients[9]
sizes = aims.Point3df(a1, a2, a3)
if (self._tapering):
# Apply the tapering transformation
sizes = self._taperingTransform.transform(sizes)
if (self._bending and (kapa != 0)):
results = self.getBendingMinMax(sizes)
start = results[0]
start[2] = - self.getZMax()
else:
start = sizes * -1
return start
"""
Get bounding box sizes for the current superquadric.
@rtype: aims.Point3df
@return: sizes of the bounding box for the superquadric.
"""
def getBoundingBoxSizes(self):
e1 = self._coefficients[0]
e2 = self._coefficients[1]
a1 = self._coefficients[2]
a2 = self._coefficients[3]
a3 = self._coefficients[4]
alpha = self._coefficients[8]
kapa = self._coefficients[9]
sizes = aims.Point3df(a1, a2, a3)
if (self._tapering):
# Apply the tapering transformation
sizes = self._taperingTransform.transform(sizes)
if (self._bending and (kapa != 0)):
results = self.getBendingMinMax(sizes)
minimum = results[0]
maximum = results[1]
sizes[0] = (maximum[0] - minimum[0])
sizes[1] = (maximum[1] - minimum[1])
sizes[2] = 2 * self.getZMax()
else:
sizes *= 2
return sizes
"""
Get maximum z value for the deformable superquadric after transformation.
@rtype: float
@return: maximum z value for the superquadric.
"""
def getZMax(self):
e1 = self._coefficients[0]
e2 = self._coefficients[1]
a1 = self._coefficients[2]
a2 = self._coefficients[3]
a3 = self._coefficients[4]
alpha = self._coefficients[8]
kapa = self._coefficients[9]
sizes = aims.Point3df(a1, a2, a3)
if (self._tapering):
# Apply the tapering transformation to coefficients
sizes = self._taperingTransform.transform(sizes)
a1 = sizes[0]
a2 = sizes[1]
a3 = sizes[2]
if (kapa != 0):
boundingRay = math.sqrt(a1**2 * (math.cos(alpha) ** 2) ** e2
+ a2**2 * (math.sin(alpha) ** 2) ** e2) / 2
zmax = (boundingRay + 1 / kapa)
else:
zmax = a3
return zmax
"""
Get minimum and maximum values for points.
@type sizes: aims.Point3df
@param sizes: list that contains 8 points of bounding box to get the bounding box
after bending transform.
@rtype: list
@return: minimum and maximum points after bending transformation applied.
"""
def getBendingMinMax(self, sizes):
# Get the 4 points of the bounding box in the plan z=0
point1 = aims.Point3df(sizes[0], sizes[1], 0)
point2 = aims.Point3df(sizes[0], -sizes[1], 0)
point3 = aims.Point3df(-sizes[0], -sizes[1], 0)
point4 = aims.Point3df(-sizes[0], sizes[1], 0)
# Get the 4 top points of the bounding box plan z=sizes[2]
point5 = aims.Point3df(sizes[0], sizes[1], sizes[2])
point6 = aims.Point3df(sizes[0], -sizes[1], sizes[2])
point7 = aims.Point3df(-sizes[0], -sizes[1], sizes[2])
point8 = aims.Point3df(-sizes[0], sizes[1], sizes[2])
# Apply the bending transformation to the 4 top points
# of the bounding box
point5 = self._bendingTransform.transform(point5)
point6 = self._bendingTransform.transform(point6)
point7 = self._bendingTransform.transform(point7)
point8 = self._bendingTransform.transform(point8)
# Get the maximum and minimum values for x, y, z
points = [point1, point2, point3, point4,
point5, point6, point7, point8]
result = self.getMinMax(points)
return result
"""
Get minimum and maximum values for points.
@type points: list
@param points: The list of points to get minimum and maximum.
@rtype: list
@return: minimum and maximum points.
"""
def getMinMax(self, points):
if points is not None:
minimum = aims.Point3df(points[0])
maximum = aims.Point3df(points[0])
for point in points:
for index in range(len(point)):
if point[index] < minimum[index]:
minimum[index] = point[index]
if point[index] > maximum[index]:
maximum[index] = point[index]
result = [minimum, maximum]
else:
result = None
return result