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