chiark / gitweb /
0de3bf67922cbe7323f93842698c4aeb0f4f24ef
[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                 glColor3ub(0,0,0)
369                 size = self.parent.getObjectSize()
370                 radius = self.parent.getObjectBoundaryCircle() * max(scaleX, scaleY, scaleZ)
371                 glPushMatrix()
372                 glTranslate(0,0,size[2]/2 + 5)
373                 glRotate(-self.parent.yaw, 0,0,1)
374                 if self.parent.pitch < 80:
375                         glTranslate(0, radius + 5,0)
376                 elif self.parent.pitch < 100:
377                         glTranslate(0, (radius + 5) * (90 - self.parent.pitch) / 10,0)
378                 else:
379                         glTranslate(0,-(radius + 5),0)
380                 opengl.glDrawStringCenter("%dx%dx%d" % (size[0] * scaleX, size[1] * scaleY, size[2] * scaleZ))
381                 glPopMatrix()
382
383                 glLineWidth(1)
384                 glBegin(GL_LINES)
385                 glColor3ub(128,0,0)
386                 glVertex3f(0, 0, 0)
387                 glVertex3f(sx, 0, 0)
388                 glColor3ub(128,128,0)
389                 glVertex3f(0, 0, 0)
390                 glVertex3f(0, sy, 0)
391                 glColor3ub(0,128,0)
392                 glVertex3f(0, 0, 0)
393                 glVertex3f(0, 0, sz)
394                 glEnd()
395
396                 glLineWidth(2)
397                 if self.node == 1:
398                         glColor3ub(255,255,255)
399                 else:
400                         glColor3ub(192,192,192)
401                 opengl.DrawBox([-s,-s,-s], [s,s,s])
402                 if self.node == 1:
403                         glColor3ub(0,0,0)
404                         opengl.glDrawStringCenter("%0.2f" % ((scaleX + scaleY + scaleZ) / 3.0))
405
406                 if self.node == 2:
407                         glColor3ub(255,64,64)
408                 else:
409                         glColor3ub(128,0,0)
410                 glPushMatrix()
411                 glTranslatef(sx,0,0)
412                 opengl.DrawBox([-s,-s,-s], [s,s,s])
413                 if self.node == 2:
414                         glColor3ub(0,0,0)
415                         opengl.glDrawStringCenter("%0.2f" % (scaleX))
416                 glPopMatrix()
417                 if self.node == 3:
418                         glColor3ub(255,255,0)
419                 else:
420                         glColor3ub(128,128,0)
421                 glPushMatrix()
422                 glTranslatef(0,sy,0)
423                 opengl.DrawBox([-s,-s,-s], [s,s,s])
424                 if self.node == 3:
425                         glColor3ub(0,0,0)
426                         opengl.glDrawStringCenter("%0.2f" % (scaleY))
427                 glPopMatrix()
428                 if self.node == 4:
429                         glColor3ub(64,255,64)
430                 else:
431                         glColor3ub(0,128,0)
432                 glPushMatrix()
433                 glTranslatef(0,0,sz)
434                 opengl.DrawBox([-s,-s,-s], [s,s,s])
435                 if self.node == 4:
436                         glColor3ub(0,0,0)
437                         opengl.glDrawStringCenter("%0.2f" % (scaleZ))
438                 glPopMatrix()
439
440                 glEnable(GL_DEPTH_TEST)