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