lsst.skymap  15.0-3-gb7a597c+10
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 numpy
7 
8 
9 class Dodecahedron(object):
10  """A dodecahedron
11 
12  Contains positions of faces and associated vertices
13  """
14 
15  def __init__(self, withFacesOnPoles=False):
16  """Construct a Dodecahedron
17 
18  @param[in] withFacesOnPoles: if True center a face on each pole, else put a vertex on each pole
19  """
20  self._withFacesOnPoles = bool(withFacesOnPoles)
21 
22  # Basis cartesian vectors describing the faces of a dodecahedron; the full set of vectors is obtained
23  # by choosing both signs of each nonzero component of each vector.
24  # The orientation of the resulting dodecahedron, while very convenient
25  # for specifying face vectors, is not an orientation we want so it must be rotated.
26  g = (1.0 + math.sqrt(5.0)) / 2.0
27  faceBases = (
28  (0, 1, g),
29  (1, g, 0),
30  (g, 0, 1),
31  )
32  unrotFaceVecList = _computeFullVecList(faceBases)
33  unrotVertexVecList = _computeDodecahedronVertices(unrotFaceVecList)
34 
35  if self._withFacesOnPoles:
36  # one face is centered on each pole
37  vec0, vec1 = _findClosePair(unrotFaceVecList, 0)
38  rotMat = _computeCoordTransform(vec0, vec1)
39  else:
40  # one vertex is on each pole
41  vec0, vec1 = _findClosePair(unrotVertexVecList, 0)
42  rotMat = _computeCoordTransform(vec0, vec1, vec1NegativeX=True)
43  self.vertexVecList = [numpy.dot(rotMat, unrotVertexVec) for unrotVertexVec in unrotVertexVecList]
44  unsortedFaceList = [numpy.dot(rotMat, unrotFaceVec) for unrotFaceVec in unrotFaceVecList]
45  self.faceVecList = _sortedVectorList(unsortedFaceList)
46 
47  def getFaceCtrList(self):
48  """Return a list of face centers
49 
50  @return a list of face centers (in index order); each a unit vector (numpy array)
51  """
52  return self.faceVecList[:]
53 
54  def getFaceCtr(self, ind):
55  """Return the center of the specified face
56 
57  @param[in] ind: face index
58  @return face center as a unit vector (numpy array)
59  """
60  return self.faceVecList[ind][:]
61 
62  def getVertices(self, ind):
63  """Return the vertices for a given face
64 
65  @param[in] ind: face index
66  @return a list of vertices, each a unit vector (numpy array)
67  """
68  faceVec = self.getFaceCtr(ind)
69  vertexList, indList = _findCloseList(self.vertexVecList, faceVec)
70 
71  # sort vertex list about face vector (direction is random)
72  sortedVertexList = [vertexList[0]]
73  vertexList = list(vertexList[1:])
74  while len(vertexList) != 0:
75  nearVertexList, nearInd = _findCloseList(vertexList, sortedVertexList[-1])
76  sortedVertexList.append(nearVertexList[0])
77  vertexList.pop(nearInd[0])
78  return sortedVertexList
79 
80  def getFaceInd(self, vec):
81  """Return the index of the face containing the cartesian vector
82 
83  @param[in] vec: cartesian vector (length is ignored)
84  @return index of face containing vec
85  """
86  return numpy.argmax(numpy.dot(self.faceVecList, vec))
87 
89  """Get withFacesOnPoles parameter
90  """
91  return self._withFacesOnPoles
92 
93 
94 def computeRotationMatrix(angle, axis):
95  """Return a 3D rotation matrix for rotation by a specified amount around a specified axis
96 
97  Inputs:
98  - angle: amount of rotation (rad)
99  - axis: axis of rotation; one of 0, 1 or 2 for x, y or z
100  """
101  cosAng = math.cos(angle)
102  sinAng = math.sin(angle)
103  rotMat = numpy.zeros((3, 3), dtype=float)
104  rotMat[axis, axis] = 1
105  rotMat[(axis + 1) % 3, (axis + 1) % 3] = cosAng
106  rotMat[(axis + 2) % 3, (axis + 1) % 3] = sinAng
107  rotMat[(axis + 1) % 3, (axis + 2) % 3] = -sinAng
108  rotMat[(axis + 2) % 3, (axis + 2) % 3] = cosAng
109  return rotMat
110 
111 
112 def _computeCoordTransform(vec0, vec1, vec1NegativeX=False):
113  """Compute a rotation matrix that puts vec0 along z and vec1 along +x in the xz plane
114 
115  Inputs:
116  - vec0: vector 0
117  - vec1: vector 1
118  - vec1NegativeX: if True then vec1 is rotated to face negative x
119  """
120  # rotate around x by angle of vec0 from z to y
121  xAng = math.atan2(vec0[1], vec0[2])
122  xRotMat = computeRotationMatrix(xAng, 0)
123 
124  # rotate around y by -angle of rotated vec0 from z to x
125  vec0RotX = numpy.dot(xRotMat, vec0)
126  yAng = -math.atan2(vec0RotX[0], vec0RotX[2])
127  yRotMat = computeRotationMatrix(yAng, 1)
128  xyRotMat = numpy.dot(yRotMat, xRotMat)
129 
130  # rotate around z by -angle of rotated vec1 from +/-x to y
131  vec1RotXY = numpy.dot(xyRotMat, vec1)
132  xVal = vec1RotXY[0]
133  if vec1NegativeX:
134  xVal = -xVal
135  zAng = -math.atan2(vec1RotXY[1], xVal)
136  zRotMat = computeRotationMatrix(zAng, 2)
137  xyzRotMat = numpy.dot(zRotMat, xyRotMat)
138  return xyzRotMat
139 
140 
141 def _computeDodecahedronVertices(faceVecList):
142  """Given a vector of face positions of a Dodecahedron compute the vertices
143  """
144  closeIndSetList = []
145  vertexDict = {}
146  for i in range(len(faceVecList)):
147  closeIndSet = _findCloseIndexSet(faceVecList, i)
148  if len(closeIndSet) != 5:
149  raise RuntimeError("Found %s vertices instead of 5 near %s: %s" %
150  (len(closeIndSet), faceVecList[i], closeIndSet))
151  closeIndSetList.append(closeIndSet)
152  for i, iCloseIndSet in enumerate(closeIndSetList):
153  for j in iCloseIndSet:
154  jCloseIndSet = closeIndSetList[j]
155  sharedCloseIndSet = iCloseIndSet.intersection(jCloseIndSet)
156  if len(sharedCloseIndSet) != 2:
157  raise RuntimeError("Found %s vertices instead of 2 near %s and %s: %s" %
158  (len(sharedCloseIndSet), faceVecList[i], faceVecList[j],
159  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 
255 if __name__ == "__main__":
256  numpy.set_printoptions(precision=2, suppress=True, linewidth=120)
257 
258  print("Dodecahedron with vertices on poles")
259  vertexDodec = Dodecahedron(withFacesOnPoles=False)
260  for i in range(12):
261  faceVec = vertexDodec.getFaceCtr(i)
262  print("Face %2d: %s" % (i, faceVec))
def computeRotationMatrix(angle, axis)
Definition: dodecahedron.py:94
def __init__(self, withFacesOnPoles=False)
Definition: dodecahedron.py:15