lsst.skymap  13.0-5-g2a40766+27
dodecahedron.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from __future__ import print_function
3 from builtins import range
4 from builtins import object
5 import math
6 import itertools
7 import numpy
8 
9 
10 class Dodecahedron(object):
11  """A dodecahedron
12 
13  Contains positions of faces and associated vertices
14  """
15 
16  def __init__(self, withFacesOnPoles=False):
17  """Construct a Dodecahedron
18 
19  @param[in] withFacesOnPoles: if True center a face on each pole, else put a vertex on each pole
20  """
21  self._withFacesOnPoles = bool(withFacesOnPoles)
22 
23  # Basis cartesian vectors describing the faces of a dodecahedron; the full set of vectors is obtained
24  # by choosing both signs of each nonzero component of each vector.
25  # The orientation of the resulting dodecahedron, while very convenient
26  # for specifying face vectors, is not an orientation we want so it must be rotated.
27  g = (1.0 + math.sqrt(5.0)) / 2.0
28  faceBases = (
29  (0, 1, g),
30  (1, g, 0),
31  (g, 0, 1),
32  )
33  unrotFaceVecList = _computeFullVecList(faceBases)
34  unrotVertexVecList = _computeDodecahedronVertices(unrotFaceVecList)
35 
36  if self._withFacesOnPoles:
37  # one face is centered on each pole
38  vec0, vec1 = _findClosePair(unrotFaceVecList, 0)
39  rotMat = _computeCoordTransform(vec0, vec1)
40  else:
41  # one vertex is on each pole
42  vec0, vec1 = _findClosePair(unrotVertexVecList, 0)
43  rotMat = _computeCoordTransform(vec0, vec1, vec1NegativeX=True)
44  self.vertexVecList = [numpy.dot(rotMat, unrotVertexVec) for unrotVertexVec in unrotVertexVecList]
45  unsortedFaceList = [numpy.dot(rotMat, unrotFaceVec) for unrotFaceVec in unrotFaceVecList]
46  self.faceVecList = _sortedVectorList(unsortedFaceList)
47 
48  def getFaceCtrList(self):
49  """Return a list of face centers
50 
51  @return a list of face centers (in index order); each a unit vector (numpy array)
52  """
53  return self.faceVecList[:]
54 
55  def getFaceCtr(self, ind):
56  """Return the center of the specified face
57 
58  @param[in] ind: face index
59  @return face center as a unit vector (numpy array)
60  """
61  return self.faceVecList[ind][:]
62 
63  def getVertices(self, ind):
64  """Return the vertices for a given face
65 
66  @param[in] ind: face index
67  @return a list of vertices, each a unit vector (numpy array)
68  """
69  faceVec = self.getFaceCtr(ind)
70  vertexList, indList = _findCloseList(self.vertexVecList, faceVec)
71 
72  # sort vertex list about face vector (direction is random)
73  sortedVertexList = [vertexList[0]]
74  vertexList = list(vertexList[1:])
75  while len(vertexList) != 0:
76  nearVertexList, nearInd = _findCloseList(vertexList, sortedVertexList[-1])
77  sortedVertexList.append(nearVertexList[0])
78  vertexList.pop(nearInd[0])
79  return sortedVertexList
80 
81  def getFaceInd(self, vec):
82  """Return the index of the face containing the cartesian vector
83 
84  @param[in] vec: cartesian vector (length is ignored)
85  @return index of face containing vec
86  """
87  return numpy.argmax(numpy.dot(self.faceVecList, vec))
88 
90  """Get withFacesOnPoles parameter
91  """
92  return self._withFacesOnPoles
93 
94 
95 def computeRotationMatrix(angle, axis):
96  """Return a 3D rotation matrix for rotation by a specified amount around a specified axis
97 
98  Inputs:
99  - angle: amount of rotation (rad)
100  - axis: axis of rotation; one of 0, 1 or 2 for x, y or z
101  """
102  cosAng = math.cos(angle)
103  sinAng = math.sin(angle)
104  rotMat = numpy.zeros((3, 3), dtype=float)
105  rotMat[axis, axis] = 1
106  rotMat[(axis + 1) % 3, (axis + 1) % 3] = cosAng
107  rotMat[(axis + 2) % 3, (axis + 1) % 3] = sinAng
108  rotMat[(axis + 1) % 3, (axis + 2) % 3] = -sinAng
109  rotMat[(axis + 2) % 3, (axis + 2) % 3] = cosAng
110  return rotMat
111 
112 
113 def _computeCoordTransform(vec0, vec1, vec1NegativeX=False):
114  """Compute a rotation matrix that puts vec0 along z and vec1 along +x in the xz plane
115 
116  Inputs:
117  - vec0: vector 0
118  - vec1: vector 1
119  - vec1NegativeX: if True then vec1 is rotated to face negative x
120  """
121  # rotate around x by angle of vec0 from z to y
122  xAng = math.atan2(vec0[1], vec0[2])
123  xRotMat = computeRotationMatrix(xAng, 0)
124 
125  # rotate around y by -angle of rotated vec0 from z to x
126  vec0RotX = numpy.dot(xRotMat, vec0)
127  yAng = -math.atan2(vec0RotX[0], vec0RotX[2])
128  yRotMat = computeRotationMatrix(yAng, 1)
129  xyRotMat = numpy.dot(yRotMat, xRotMat)
130 
131  # rotate around z by -angle of rotated vec1 from +/-x to y
132  vec1RotXY = numpy.dot(xyRotMat, vec1)
133  xVal = vec1RotXY[0]
134  if vec1NegativeX:
135  xVal = -xVal
136  zAng = -math.atan2(vec1RotXY[1], xVal)
137  zRotMat = computeRotationMatrix(zAng, 2)
138  xyzRotMat = numpy.dot(zRotMat, xyRotMat)
139  return xyzRotMat
140 
141 
142 def _computeDodecahedronVertices(faceVecList):
143  """Given a vector of face positions of a Dodecahedron compute the vertices
144  """
145  closeIndSetList = []
146  vertexDict = {}
147  for i in range(len(faceVecList)):
148  closeIndSet = _findCloseIndexSet(faceVecList, i)
149  if len(closeIndSet) != 5:
150  raise RuntimeError("Found %s vertices instead of 5 near %s: %s" %
151  (len(closeIndSet), faceVecList[i], closeIndSet))
152  closeIndSetList.append(closeIndSet)
153  for i, iCloseIndSet in enumerate(closeIndSetList):
154  for j in iCloseIndSet:
155  jCloseIndSet = closeIndSetList[j]
156  sharedCloseIndSet = iCloseIndSet.intersection(jCloseIndSet)
157  if len(sharedCloseIndSet) != 2:
158  raise RuntimeError("Found %s vertices instead of 2 near %s and %s: %s" %
159  (len(sharedCloseIndSet), faceVecList[i], faceVecList[j], sharedCloseIndSet))
160  for k in sharedCloseIndSet:
161  key = frozenset((i, j, k))
162  if key in vertexDict:
163  continue
164  vertexVec = faceVecList[i] + faceVecList[j] + faceVecList[k]
165  vertexVec /= numpy.sqrt(numpy.sum(vertexVec**2))
166  vertexDict[key] = vertexVec
167  return list(vertexDict.values())
168 
169 
170 def _computeFullVecList(basisSet):
171  """Given a collection of basis vectors, compute all permutations with both signs of all nonzero values
172 
173  For example: [(0, 1, 2)] -> [(0, 1, 2), (0, -1, 2), (0, 1, -2), (0, -1, -2)]
174  """
175  fullSet = []
176  for basisVec in basisSet:
177  vecLen = math.sqrt(numpy.sum(numpy.array(basisVec)**2))
178  valueList = []
179  for basisValue in basisVec:
180  if basisValue == 0:
181  valueList.append((0,))
182  else:
183  valueList.append((basisValue, -basisValue))
184  fullSet += list(numpy.array((x, y, z))/vecLen
185  for z in valueList[2]
186  for y in valueList[1]
187  for x in valueList[0]
188  )
189  return fullSet
190 
191 
192 def _findCloseIndexSet(vecList, ind):
193  """Given a list of cartesian vectors, return a set of indices of those closest to one of them
194 
195  This is intended for regular grids where distances are quantized
196 
197  Inputs:
198  - vecList: list of cartesian vectors
199  - ind: index of vector to be nearest
200  """
201  dotProductList = numpy.round(numpy.dot(vecList, vecList[ind]), 2)
202  dotProductList[ind] = -9e99
203  minDist = numpy.max(dotProductList)
204  indList = numpy.arange(len(dotProductList))[dotProductList == minDist]
205  return set(indList)
206 
207 
208 def _findCloseList(vecList, vec):
209  """Given a list of cartesian vectors, return all those closest to a specified position
210 
211  This is intended for regular grids where distances are quantized
212 
213  Inputs:
214  - vecList: list of cartesian vectors
215  - vec: vector to be near
216 
217  @return two items:
218  - list of closest vectors
219  - list if indices of those vectors
220  """
221  dotProductList = numpy.round(numpy.dot(vecList, vec), 2)
222  minDist = numpy.max(dotProductList)
223  indList = numpy.arange(len(dotProductList))[dotProductList == minDist]
224  retList = numpy.take(vecList, indList, 0)
225  return retList, indList
226 
227 
228 def _findClosePair(vecList, ind=0):
229  """Given a list of cartesian vectors and an index, return the vector and one of its closest neighbors
230 
231  Inputs:
232  - vecList: list of cartesian vectors
233  - ind: index of first vector
234  """
235  vec = vecList[ind]
236  otherVecList = vecList[0:ind] + vecList[ind+1:]
237  ind1 = numpy.argmax(numpy.dot(otherVecList, vec))
238  return vec, otherVecList[ind1]
239 
240 
241 def _sortedVectorList(vecList):
242  """Return a list of cartesian vectors sorted by decreasing latitude and increasing longitude
243  """
244  def vecToSort(vec):
245  ang = round(math.atan2(vec[1], vec[0]), 2)
246  if ang < 0:
247  ang += 2.0 * math.pi
248  return (-round(vec[2], 1), ang, vec)
249 
250  decoratedList = [vecToSort(v) for v in vecList]
251  decoratedList.sort()
252  return [d[2] for d in decoratedList]
253 
254 if __name__ == "__main__":
255  numpy.set_printoptions(precision=2, suppress=True, linewidth=120)
256  import lsst.afw.coord as afwCoord
257  import lsst.afw.geom as afwGeom
258 
259  print("Dodecahedron with vertices on poles")
260  vertexDodec = Dodecahedron(withFacesOnPoles=False)
261  for i in range(12):
262  faceVec = vertexDodec.getFaceCtr(i)
263  print("Face %2d: %s" % (i, faceVec))
def computeRotationMatrix(angle, axis)
Definition: dodecahedron.py:95
def __init__(self, withFacesOnPoles=False)
Definition: dodecahedron.py:16