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