chiark / gitweb /
Split off some code from preview3d.py, which is becoming way to large.
[cura.git] / Cura / gui / util / previewTools.py
1 from __future__ import absolute_import
2
3 import math
4 import wx
5 import numpy
6
7 import OpenGL
8 OpenGL.ERROR_CHECKING = False
9 from OpenGL.GLU import *
10 from OpenGL.GL import *
11
12 from Cura.gui.util import opengl
13
14 class toolInfo(object):
15         def __init__(self, parent):
16                 self.parent = parent
17
18         def OnMouseMove(self, p0, p1):
19                 pass
20
21         def OnDragStart(self, p0, p1):
22                 return False
23
24         def OnDrag(self, p0, p1):
25                 pass
26
27         def OnDragEnd(self):
28                 pass
29
30         def OnDraw(self):
31                 glDisable(GL_LIGHTING)
32                 glDisable(GL_BLEND)
33                 glDisable(GL_DEPTH_TEST)
34                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
35                 glColor3ub(0,0,0)
36                 size = self.parent.getObjectSize()
37                 radius = self.parent.getObjectBoundaryCircle()
38                 glPushMatrix()
39                 glTranslate(0,0,size[2]/2 + 5)
40                 glRotate(-self.parent.yaw, 0,0,1)
41                 if self.parent.pitch < 80:
42                         glTranslate(0, radius + 5,0)
43                 elif self.parent.pitch < 100:
44                         glTranslate(0, (radius + 5) * (90 - self.parent.pitch) / 10,0)
45                 else:
46                         glTranslate(0,-(radius + 5),0)
47                 opengl.glDrawStringCenter("%dx%dx%d" % (size[0], size[1], size[2]))
48                 glPopMatrix()
49
50                 glColor(255,255,255)
51                 size = size / 2
52                 glLineWidth(1)
53                 glBegin(GL_LINES)
54                 glVertex3f(size[0], size[1], size[2])
55                 glVertex3f(size[0], size[1], size[2]/4*3)
56                 glVertex3f(size[0], size[1], size[2])
57                 glVertex3f(size[0], size[1]/4*3, size[2])
58                 glVertex3f(size[0], size[1], size[2])
59                 glVertex3f(size[0]/4*3, size[1], size[2])
60
61                 glVertex3f(-size[0], -size[1], size[2])
62                 glVertex3f(-size[0], -size[1], size[2]/4*3)
63                 glVertex3f(-size[0], -size[1], size[2])
64                 glVertex3f(-size[0], -size[1]/4*3, size[2])
65                 glVertex3f(-size[0], -size[1], size[2])
66                 glVertex3f(-size[0]/4*3, -size[1], size[2])
67
68                 glVertex3f(size[0], -size[1], -size[2])
69                 glVertex3f(size[0], -size[1], -size[2]/4*3)
70                 glVertex3f(size[0], -size[1], -size[2])
71                 glVertex3f(size[0], -size[1]/4*3, -size[2])
72                 glVertex3f(size[0], -size[1], -size[2])
73                 glVertex3f(size[0]/4*3, -size[1], -size[2])
74
75                 glVertex3f(-size[0], size[1], -size[2])
76                 glVertex3f(-size[0], size[1], -size[2]/4*3)
77                 glVertex3f(-size[0], size[1], -size[2])
78                 glVertex3f(-size[0], size[1]/4*3, -size[2])
79                 glVertex3f(-size[0], size[1], -size[2])
80                 glVertex3f(-size[0]/4*3, size[1], -size[2])
81                 glEnd()
82
83 class toolRotate(object):
84         def __init__(self, parent):
85                 self.parent = parent
86                 self.rotateRingDist = 1.5
87                 self.rotateRingDistMin = 1.3
88                 self.rotateRingDistMax = 1.7
89                 self.dragPlane = None
90                 self.dragStartAngle = None
91                 self.dragEndAngle = None
92
93         def _ProjectToPlanes(self, p0, p1):
94                 pp0 = p0 - [0,0,self.parent.getObjectSize()[2]/2]
95                 pp1 = p1 - [0,0,self.parent.getObjectSize()[2]/2]
96                 cursorX0 = pp0 - (pp1 - pp0) * (pp0[0] / (pp1[0] - pp0[0]))
97                 cursorY0 = pp0 - (pp1 - pp0) * (pp0[1] / (pp1[1] - pp0[1]))
98                 cursorZ0 = pp0 - (pp1 - pp0) * (pp0[2] / (pp1[2] - pp0[2]))
99                 cursorYZ = math.sqrt((cursorX0[1] * cursorX0[1]) + (cursorX0[2] * cursorX0[2]))
100                 cursorXZ = math.sqrt((cursorY0[0] * cursorY0[0]) + (cursorY0[2] * cursorY0[2]))
101                 cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1]))
102                 return cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY
103
104         def OnMouseMove(self, p0, p1):
105                 radius = self.parent.getObjectBoundaryCircle()
106                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
107                 oldDragPlane = self.dragPlane
108                 if radius * self.rotateRingDistMin <= cursorXY <= radius * self.rotateRingDistMax or radius * self.rotateRingDistMin <= cursorYZ <= radius * self.rotateRingDistMax or radius * self.rotateRingDistMin <= cursorXZ <= radius * self.rotateRingDistMax:
109                         self.parent.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
110                         if self.dragStartAngle is None:
111                                 if radius * self.rotateRingDistMin <= cursorXY <= radius * self.rotateRingDistMax:
112                                         self.dragPlane = 'XY'
113                                 elif radius * self.rotateRingDistMin <= cursorXZ <= radius * self.rotateRingDistMax:
114                                         self.dragPlane = 'XZ'
115                                 else:
116                                         self.dragPlane = 'YZ'
117                 else:
118                         if self.dragStartAngle is None:
119                                 self.dragPlane = ''
120                         self.parent.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
121
122         def OnDragStart(self, p0, p1):
123                 radius = self.parent.getObjectBoundaryCircle()
124                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
125                 if radius * self.rotateRingDistMin <= cursorXY <= radius * self.rotateRingDistMax or radius * self.rotateRingDistMin <= cursorYZ <= radius * self.rotateRingDistMax or radius * self.rotateRingDistMin <= cursorXZ <= radius * self.rotateRingDistMax:
126                         if radius * self.rotateRingDistMin <= cursorXY <= radius * self.rotateRingDistMax:
127                                 self.dragPlane = 'XY'
128                                 self.dragStartAngle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi
129                         elif radius * self.rotateRingDistMin <= cursorXZ <= radius * self.rotateRingDistMax:
130                                 self.dragPlane = 'XZ'
131                                 self.dragStartAngle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi
132                         else:
133                                 self.dragPlane = 'YZ'
134                                 self.dragStartAngle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi
135                         return True
136                 return False
137
138         def OnDrag(self, p0, p1):
139                 cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1)
140                 if self.dragPlane == 'XY':
141                         angle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi
142                 elif self.dragPlane == 'XZ':
143                         angle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi
144                 else:
145                         angle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi
146                 diff = angle - self.dragStartAngle
147                 if wx.GetKeyState(wx.WXK_SHIFT):
148                         diff = round(diff / 1) * 1
149                 else:
150                         diff = round(diff / 15) * 15
151                 if diff > 180:
152                         diff -= 360
153                 if diff < -180:
154                         diff += 360
155                 rad = diff / 180.0 * math.pi
156                 self.dragEndAngle = self.dragStartAngle + diff
157                 if self.dragPlane == 'XY':
158                         self.parent.tempMatrix = numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
159                 elif self.dragPlane == 'XZ':
160                         self.parent.tempMatrix = numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
161                 else:
162                         self.parent.tempMatrix = numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
163
164         def OnDragEnd(self):
165                 self.dragStartAngle = None
166
167         def OnDraw(self):
168                 glDisable(GL_LIGHTING)
169                 glDisable(GL_BLEND)
170                 glDisable(GL_DEPTH_TEST)
171                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
172                 radius = self.parent.getObjectBoundaryCircle()
173                 glScalef(self.rotateRingDist * radius, self.rotateRingDist * radius, self.rotateRingDist * radius)
174                 if self.dragPlane == 'XY':
175                         glLineWidth(3)
176                         glColor4ub(255,64,64,255)
177                         if self.dragStartAngle is not None:
178                                 glPushMatrix()
179                                 glRotate(self.dragStartAngle, 0,0,1)
180                                 glBegin(GL_LINES)
181                                 glVertex3f(0,0,0)
182                                 glVertex3f(1,0,0)
183                                 glEnd()
184                                 glPopMatrix()
185                                 glPushMatrix()
186                                 glRotate(self.dragEndAngle, 0,0,1)
187                                 glBegin(GL_LINES)
188                                 glVertex3f(0,0,0)
189                                 glVertex3f(1,0,0)
190                                 glEnd()
191                                 glTranslatef(1.1,0,0)
192                                 glColor4ub(0,0,0,255)
193                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
194                                 glColor4ub(255,64,64,255)
195                                 glPopMatrix()
196                 else:
197                         glLineWidth(1)
198                         glColor4ub(128,0,0,255)
199                 glBegin(GL_LINE_LOOP)
200                 for i in xrange(0, 64):
201                         glVertex3f(math.cos(i/32.0*math.pi), math.sin(i/32.0*math.pi),0)
202                 glEnd()
203                 if self.dragPlane == 'YZ':
204                         glColor4ub(64,255,64,255)
205                         glLineWidth(3)
206                         if self.dragStartAngle is not None:
207                                 glPushMatrix()
208                                 glRotate(self.dragStartAngle, 1,0,0)
209                                 glBegin(GL_LINES)
210                                 glVertex3f(0,0,0)
211                                 glVertex3f(0,1,0)
212                                 glEnd()
213                                 glPopMatrix()
214                                 glPushMatrix()
215                                 glRotate(self.dragEndAngle, 1,0,0)
216                                 glBegin(GL_LINES)
217                                 glVertex3f(0,0,0)
218                                 glVertex3f(0,1,0)
219                                 glEnd()
220                                 glTranslatef(0,1.1,0)
221                                 glColor4ub(0,0,0,255)
222                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
223                                 glColor4ub(64,255,64,255)
224                                 glPopMatrix()
225                 else:
226                         glColor4ub(0,128,0,255)
227                         glLineWidth(1)
228                 glBegin(GL_LINE_LOOP)
229                 for i in xrange(0, 64):
230                         glVertex3f(0, math.cos(i/32.0*math.pi), math.sin(i/32.0*math.pi))
231                 glEnd()
232                 if self.dragPlane == 'XZ':
233                         glLineWidth(3)
234                         glColor4ub(255,255,0,255)
235                         if self.dragStartAngle is not None:
236                                 glPushMatrix()
237                                 glRotate(self.dragStartAngle, 0,-1,0)
238                                 glBegin(GL_LINES)
239                                 glVertex3f(0,0,0)
240                                 glVertex3f(1,0,0)
241                                 glEnd()
242                                 glPopMatrix()
243                                 glPushMatrix()
244                                 glRotate(self.dragEndAngle, 0,-1,0)
245                                 glBegin(GL_LINES)
246                                 glVertex3f(0,0,0)
247                                 glVertex3f(1,0,0)
248                                 glEnd()
249                                 glTranslatef(1.1,0,0)
250                                 glColor4ub(0,0,0,255)
251                                 opengl.glDrawStringCenter("%d" % (abs(self.dragEndAngle - self.dragStartAngle)))
252                                 glColor4ub(255,255,0,255)
253                                 glPopMatrix()
254                 else:
255                         glColor4ub(128,128,0,255)
256                         glLineWidth(1)
257                 glBegin(GL_LINE_LOOP)
258                 for i in xrange(0, 64):
259                         glVertex3f(math.cos(i/32.0*math.pi), 0, math.sin(i/32.0*math.pi))
260                 glEnd()
261                 glEnable(GL_DEPTH_TEST)
262
263 class toolScale(object):
264         def __init__(self, parent):
265                 self.parent = parent
266                 self.node = None
267                 self.scale = None
268
269         def _pointDist(self, p0, p1, p2):
270                 return numpy.linalg.norm(numpy.cross((p0 - p1), (p0 - p2))) / numpy.linalg.norm(p2 - p1)
271
272         def _traceNodes(self, p0, p1):
273                 pp0 = p0 - [0,0,self.parent.getObjectSize()[2]/2]
274                 pp1 = p1 - [0,0,self.parent.getObjectSize()[2]/2]
275                 s = self._nodeSize()
276                 if self._pointDist(numpy.array([0,0,0],numpy.float32), pp0, pp1) < s * 2:
277                         return 1
278                 if self._pointDist(numpy.array([s*15,0,0],numpy.float32), pp0, pp1) < s * 2:
279                         return 2
280                 if self._pointDist(numpy.array([0,s*15,0],numpy.float32), pp0, pp1) < s * 2:
281                         return 3
282                 if self._pointDist(numpy.array([0,0,s*15],numpy.float32), pp0, pp1) < s * 2:
283                         return 4
284                 return None
285
286         def _lineLineCrossingDistOnLine(self, s0, e0, s1, e1):
287                 d0 = e0 - s0
288                 d1 = e1 - s1
289                 a = numpy.dot(d0, d0)
290                 b = numpy.dot(d0, d1)
291                 e = numpy.dot(d1, d1)
292                 d = a*e - b*b
293
294                 r = s0 - s1
295                 c = numpy.dot(d0, r)
296                 f = numpy.dot(d1, r)
297
298                 s = (b*f - c*e) / d
299                 t = (a*f - b*c) / d
300                 return t
301
302         def _nodeSize(self):
303                 return self.parent.zoom / self.parent.GetSize().GetWidth() * 6
304
305         def OnMouseMove(self, p0, p1):
306                 self.node = self._traceNodes(p0, p1)
307
308         def OnDragStart(self, p0, p1):
309                 if self.node is None:
310                         return False
311                 return True
312
313         def OnDrag(self, p0, p1):
314                 s = self._nodeSize()
315                 pp0 = p0 - [0,0,self.parent.getObjectSize()[2]/2]
316                 pp1 = p1 - [0,0,self.parent.getObjectSize()[2]/2]
317                 endPoint = [1,1,1]
318                 if self.node == 2:
319                         endPoint = [1,0,0]
320                 elif self.node == 3:
321                         endPoint = [0,1,0]
322                 elif self.node == 4:
323                         endPoint = [0,0,1]
324                 scale = self._lineLineCrossingDistOnLine(pp0, pp1, numpy.array([0,0,0], numpy.float32), numpy.array(endPoint, numpy.float32)) / 15.0 / s
325                 if not wx.GetKeyState(wx.WXK_SHIFT):
326                         scale = round(scale * 10) / 10
327                 if scale < 0:
328                         scale = -scale
329                 if scale < 0.1:
330                         scale = 0.1
331                 self.scale = scale
332                 if self.node == 1 or not wx.GetKeyState(wx.WXK_CONTROL):
333                         self.parent.tempMatrix = numpy.matrix([[scale,0,0], [0, scale, 0], [0, 0, scale]], numpy.float64)
334                 elif self.node == 2:
335                         self.parent.tempMatrix = numpy.matrix([[scale,0,0], [0, 1, 0], [0, 0, 1]], numpy.float64)
336                 elif self.node == 3:
337                         self.parent.tempMatrix = numpy.matrix([[1,0,0], [0, scale, 0], [0, 0, 1]], numpy.float64)
338                 elif self.node == 4:
339                         self.parent.tempMatrix = numpy.matrix([[1,0,0], [0, 1, 0], [0, 0, scale]], numpy.float64)
340
341         def OnDragEnd(self):
342                 self.scale = None
343
344         def OnDraw(self):
345                 s = self._nodeSize()
346                 sx = s*15
347                 sy = s*15
348                 sz = s*15
349                 if self.node == 2 and self.scale is not None:
350                         sx *= self.scale
351                 if self.node == 3 and self.scale is not None:
352                         sy *= self.scale
353                 if self.node == 4 and self.scale is not None:
354                         sz *= self.scale
355                 scaleX = numpy.linalg.norm(self.parent.parent.matrix[0].getA().flatten())
356                 scaleY = numpy.linalg.norm(self.parent.parent.matrix[1].getA().flatten())
357                 scaleZ = numpy.linalg.norm(self.parent.parent.matrix[2].getA().flatten())
358                 if self.scale is not None:
359                         scaleX *= self.scale
360                         scaleY *= self.scale
361                         scaleZ *= self.scale
362
363                 glDisable(GL_LIGHTING)
364                 glDisable(GL_DEPTH_TEST)
365                 glEnable(GL_BLEND)
366                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
367
368                 glLineWidth(1)
369                 glBegin(GL_LINES)
370                 glColor3ub(128,0,0)
371                 glVertex3f(0, 0, 0)
372                 glVertex3f(sx, 0, 0)
373                 glColor3ub(128,128,0)
374                 glVertex3f(0, 0, 0)
375                 glVertex3f(0, sy, 0)
376                 glColor3ub(0,128,0)
377                 glVertex3f(0, 0, 0)
378                 glVertex3f(0, 0, sz)
379                 glEnd()
380
381                 glLineWidth(2)
382                 if self.node == 1:
383                         glColor3ub(255,255,255)
384                 else:
385                         glColor3ub(192,192,192)
386                 opengl.DrawBox([-s,-s,-s], [s,s,s])
387                 if self.node == 1:
388                         glColor3ub(0,0,0)
389                         opengl.glDrawStringCenter("%0.2f" % ((scaleX + scaleY + scaleZ) / 3.0))
390
391                 if self.node == 2:
392                         glColor3ub(255,64,64)
393                 else:
394                         glColor3ub(128,0,0)
395                 glPushMatrix()
396                 glTranslatef(sx,0,0)
397                 opengl.DrawBox([-s,-s,-s], [s,s,s])
398                 if self.node == 2:
399                         glColor3ub(0,0,0)
400                         opengl.glDrawStringCenter("%0.2f" % (scaleX))
401                 glPopMatrix()
402                 if self.node == 3:
403                         glColor3ub(255,255,0)
404                 else:
405                         glColor3ub(128,128,0)
406                 glPushMatrix()
407                 glTranslatef(0,sy,0)
408                 opengl.DrawBox([-s,-s,-s], [s,s,s])
409                 if self.node == 3:
410                         glColor3ub(0,0,0)
411                         opengl.glDrawStringCenter("%0.2f" % (scaleY))
412                 glPopMatrix()
413                 if self.node == 4:
414                         glColor3ub(64,255,64)
415                 else:
416                         glColor3ub(0,128,0)
417                 glPushMatrix()
418                 glTranslatef(0,0,sz)
419                 opengl.DrawBox([-s,-s,-s], [s,s,s])
420                 if self.node == 4:
421                         glColor3ub(0,0,0)
422                         opengl.glDrawStringCenter("%0.2f" % (scaleZ))
423                 glPopMatrix()
424
425                 glEnable(GL_DEPTH_TEST)