1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    test_glcanvas.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16'''
17@author: Stou Sandalski (stou@icapsid.net)
18@license:  Public Domain
19'''
20
21# Uncomment if you have multiple wxWidgets versions
22#import wxversion
23# wxversion.select('2.8')
24
25import math
26import wx
27
28from wx import glcanvas
29from wx.lib.buttons import GenBitmapTextButton, GenBitmapButton
30try:
31    from OpenGL.GL import *
32    from OpenGL.GLU import *
33    from OpenGL.GLUT import *
34    from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
35        glBindVertexArray
36
37    from OpenGL.arrays import vbo
38    import numpy as np
39
40except ImportError:
41    raise ImportError, "Required dependency OpenGL not present"
42
43import sys
44import os
45import types
46APPDIR = os.path.join(os.path.dirname(__file__), "..")
47sys.path.append(os.path.join(APPDIR, "lib_base"))
48IMAGEDIR = os.path.join(os.path.dirname(__file__), "images")
49import classman as cm
50
51# wx gui stuff
52from wxmisc import *
53
54import objpanel
55import wxmisc
56
57stockIDs = [
58    wx.ID_ABOUT,
59    wx.ID_ADD,
60    wx.ID_APPLY,
61    wx.ID_BOLD,
62    wx.ID_CANCEL,
63    wx.ID_CLEAR,
64    wx.ID_CLOSE,
65    wx.ID_COPY,
66    wx.ID_CUT,
67    wx.ID_DELETE,
68    wx.ID_EDIT,
69    wx.ID_FIND,
70    wx.ID_FILE,
71    wx.ID_REPLACE,
72    wx.ID_BACKWARD,
73    wx.ID_DOWN,
74    wx.ID_FORWARD,
75    wx.ID_UP,
76    wx.ID_HELP,
77    wx.ID_HOME,
78    wx.ID_INDENT,
79    wx.ID_INDEX,
80    wx.ID_ITALIC,
81    wx.ID_JUSTIFY_CENTER,
82    wx.ID_JUSTIFY_FILL,
83    wx.ID_JUSTIFY_LEFT,
84    wx.ID_JUSTIFY_RIGHT,
85    wx.ID_NEW,
86    wx.ID_NO,
87    wx.ID_OK,
88    wx.ID_OPEN,
89    wx.ID_PASTE,
90    wx.ID_PREFERENCES,
91    wx.ID_PRINT,
92    wx.ID_PREVIEW,
93    wx.ID_PROPERTIES,
94    wx.ID_EXIT,
95    wx.ID_REDO,
96    wx.ID_REFRESH,
97    wx.ID_REMOVE,
98    wx.ID_REVERT_TO_SAVED,
99    wx.ID_SAVE,
100    wx.ID_SAVEAS,
101    wx.ID_SELECTALL,
102    wx.ID_STOP,
103    wx.ID_UNDELETE,
104    wx.ID_UNDERLINE,
105    wx.ID_UNDO,
106    wx.ID_UNINDENT,
107    wx.ID_YES,
108    wx.ID_ZOOM_100,
109    wx.ID_ZOOM_FIT,
110    wx.ID_ZOOM_IN,
111    wx.ID_ZOOM_OUT,
112
113]
114
115
116class BaseTool(cm.BaseObjman):
117    """
118    This is a base tool class for Agilecanvas.
119    It must handle all mouse or keyboard events,
120    must create and draw helplines and finally
121    modify the state of client which are grafically
122    represented on the canvas.
123    """
124
125    def __init__(self, parent, mainframe=None):
126        """
127        To be overridden by specific tool.
128        """
129        self.init_common('select', parent, 'Selection tool', mainframe, info='Select objects in cancvas')
130
131    def set_button_info(self, bsize=(32, 32)):
132        # print 'set_button_info select tool'
133        self._bitmap = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIcon.bmp'), wx.BITMAP_TYPE_BMP)
134        self._bitmap_sel = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIconSel.bmp'), wx.BITMAP_TYPE_BMP)
135
136    def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):
137
138        # simple stockbuttons
139        #b=wx.Button(parent, wx.ID_DELETE)
140
141        id = wx.NewId()
142        bitmap = self._bitmap
143
144        #b=GenBitmapTextToggleButton(parent, id, bitmap,tool.name,name = self.get_name())
145        b = GenBitmapToggleButton(parent, id, bitmap, (bitmap.GetWidth()+bottonborder,
146                                                       bitmap.GetHeight()+bottonborder), name=self.get_name())
147        #b=GenBitmapToggleButton(self, wx.ID_DELETE)
148        #b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45))
149
150        if bitmap is not None:
151            #mask = wx.Mask(bitmap, wx.BLUE)
152            # bitmap.SetMask(mask)
153            b.SetBitmapLabel(bitmap)
154            # bmp=wx.NullBitmap
155
156        bitmap_sel = self._bitmap_sel
157        if bitmap_sel is not None:
158            #mask = wx.Mask(bmp, wx.BLUE)
159            # bmp.SetMask(mask)
160            b.SetBitmapSelected(bitmap_sel)
161
162        b.SetUseFocusIndicator(False)
163
164        b.SetUseFocusIndicator(False)
165        # b.SetSize((36,140))
166        # b.SetBestSize()
167        tt = wx.ToolTip(self.get_info())
168        b.SetToolTip(tt)  # .SetTip(tool.tooltip)
169        return b
170
171    def init_common(self, ident, parent, name, mainframe=None, info=None):
172        # print 'Agiletool.__init__',ident,name
173        self.name = name
174        self.metacanvas = None
175        # FSMnamed.__init__(self,ident,parent,name)
176        self._init_objman(ident, parent=parent, name=name.title(), info=info)
177        attrsman = self.set_attrsman(cm.Attrsman(self))
178
179        self.access = attrsman.add(cm.AttrConf('access', ['bus', 'bike', 'tram'],
180                                               groupnames=['options'],
181                                               perm='rw',
182                                               is_save=True,
183                                               name='Access list',
184                                               info='List with vehicle classes that have access',
185                                               ))
186
187        self.emissiontype = attrsman.add(cm.AttrConf('emissiontype', 'Euro 0',
188                                                     groupnames=['options'],
189                                                     perm='rw',
190                                                     is_save=True,
191                                                     name='Emission type',
192                                                     info='Emission type of vehicle',
193                                                     ))
194        # dictionary of drawobjects that will be created during
195        # the application of the tool.
196        # ident is the number in chronological order of creation,
197        # starting with one. Value is the ad hoc instance of a drawing
198        # object.
199        self.drawobjs = {}
200        self.helpobjs = {}
201
202        self.mainframe = mainframe
203        # print ' call set_button',self.ident
204        self.set_button_info()
205        self.targetsets = {}
206        # self.optionspanel=None
207
208    def append_drawobj(self, drawobj):
209        """
210        Append new drawobject
211        """
212        n = len(self.drawobjs)+1
213        self.drawobjs[n] = drawobj
214
215    def pop_drawobj(self):
216        """
217        Returns most recent drawobject, removing it from the list.
218        If there are no more drwobjects in the list None is returned.
219        """
220
221        n = len(self.drawobjs)
222        if n > 0:
223            drawobj = self.drawobjs[n]
224            del self.drawobjs[n]
225            return drawobj
226        else:
227            return None
228
229    def get_last_drawobj(self):
230        """
231        Returns most recent drawobject, without changing the list.
232        If there are no more drwobjects in the list None is returned.
233        """
234        n = len(self.drawobjs)
235        if n > 0:
236            drawobj = self.drawobjs[n]
237            return drawobj
238        else:
239            return None
240
241    def clear_drawobjs(self):
242        """
243        Clear list of  drawobjects, while maintaining them on metacanvas.
244        """
245        self.drawobjs = {}
246
247    def del_drawobjs(self):
248        """
249        Remove all drawobjects from metacanvas.
250        """
251        while len(self.drawobjs) > 0:
252            drawobj = self.pop_drawobj()
253            self.metacanvas.del_obj(drawobj)
254
255    def make_targetsets(self, setnames=None, layer=None):
256        """
257        Returns a dictionary with instances of targetsets as values
258        and setnames as key.
259        This allows the tool to select a list of sets from the a specific
260        layer to which it can directly communicate.
261
262        If no setnames are given then all sets of the specific layer
263        are returned.
264
265        Can be used for example to change handle settings
266        """
267        if setnames is not None:
268            objsets = {}
269            for name in setnames:
270                objsets[name] = self.metacanvas.get_objset_from_layer(layer, name)
271            self.targetsets = objsets
272        else:
273            self.targetsets = self.metacanvas.get_objset_from_layer(layer)
274
275    def set_handles(self):
276        """
277        Set handles to selected object sets which can be connected.
278        """
279        # put handles on all section objects
280        for name_set in self.targetsets.keys():
281            self.metacanvas.set_handles(name_set=name_set)
282
283    def get_bitmap_from_file(self, name_bitmap):
284        # print 'get_bitmap_from_file :'+"gui/bitmaps/" + name_bitmap + ".bmp"
285        return wx.Bitmap("gui/bitmaps/" + name_bitmap + ".bmp",
286                         wx.BITMAP_TYPE_BMP)
287
288    # def make_optionpanel(self,panel):
289
290    def get_optionpanel(self, parent):
291        """
292        Return tool option widgets on given parent
293        """
294        # print 'get_optionpanel',self
295        #button = wx.Button(parent, wx.NewId(), self.name+' Options')
296        #button.Bind(wx.EVT_BUTTON, self.set_options)
297        # return button
298
299        self.optionspanel = ObjPanel(parent, self, groups=['options'],
300                                     standartbuttons=[],
301                                     immediate_apply=True,
302                                     panelstyle='instrumental')
303        return self.optionspanel
304
305    def set_options(self, event):
306        """
307        Called from options panel.
308        """
309        print 'set_options', self.ident
310        print '  event=', event
311        pass
312
313    def set_statusbar(self, key, info):
314        pass
315
316    def activate_metacanvas(self, metacanvas):
317        """
318        This call by metacanvas signals that the tool has been
319        activated and can now interact with metacanvas.
320        """
321        # print 'activate_metacanvas',self.ident
322        self.metacanvas = metacanvas
323        self.metacanvas.del_handles()
324        self.activate()
325
326    def get_metacanvas(self):
327        return self.metacanvas
328
329    # def get_pentable(self):
330    #    if self.metacanvas:
331    #        return self.metacanvas.get_pentable()
332
333    def deactivate_metacanvas(self):
334        """
335        This call by metacanvas signals that the tool has been
336        deactivated and can now interact with metacanvas.
337        """
338
339        self.deactivate()
340        self.optionspanel = None
341        self.metacanvas = None
342
343
344class DelTool(BaseTool):
345    def __init__(self, parent, mainframe=None):
346        """
347        To be overridden by specific tool.
348        """
349        self.init_common('delete', parent, 'Delete', mainframe, info='Delete objects in cancvas')
350
351    def set_button_info(self, bsize=(32, 32)):
352        # print 'set_button_info select tool'
353        self._bitmap = None
354        self._bitmap_sel = None
355
356    def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):
357
358        # simple stockbuttons
359        b = wx.Button(parent, wx.ID_DELETE, name=self.get_name())
360
361        b.SetSize(bottonsize)
362        # b.SetBestSize()
363        tt = wx.ToolTip(self.get_info())
364        b.SetToolTip(tt)  # .SetTip(tool.tooltip)
365        # print 'DelTool.get_button',dir(b)
366        return b
367
368
369class ToolPalett(wx.Panel):
370    """
371    This is a panel where tools are represented by images and/or text.
372    The tools are selected in a radio-button-fashion.
373
374    Each tool has a string as key. Each time the status changes,
375    a callback function is called with new and old tool key as argument.
376    """
377
378    def __init__(self, parent, tools=[], callback=None, n_buttoncolumns=4):
379        """
380        callback is a function that is called when a tool has been selected.
381        The function is called as:
382            callback(tool)
383
384        """
385        # the metacanvas object with which the pallet should apply th tools
386        self._callback = callback
387
388        # wx.Window.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS)
389        # wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,size,wx.RAISED_BORDER|wx.WANTS_CHARS)
390        wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize)
391        # wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,(300,600),wx.RAISED_BORDER|wx.WANTS_CHARS)
392        self.sizer = wx.GridSizer(0, n_buttoncolumns, 5, 5)
393        self.SetSizer(self.sizer)
394        self._id_to_tool = {}
395        self._id = -1
396
397        for tool in tools:
398            self.add_tool(tool)
399
400        self.sizer.Fit(self)
401        # self.SetMaxSize((300,300))
402
403    def add_tool(self, tool):
404        """
405        Add a tool to the pallet.
406        """
407        bottonsize = (32, 32)
408        bottonborder = 10
409        toolbarborder = 1
410
411        if tool is None:
412            self.sizer.Add()
413            return None
414        else:
415            b = tool.get_button(self, bottonsize=bottonsize, bottonborder=bottonborder)
416            self.Bind(wx.EVT_BUTTON, self.on_select, b)
417
418            self._id_to_tool[b.GetId()] = (tool, b)
419
420            #self.sizer.Add(b, 0, wx.GROW)
421            self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder)
422            # self.sizer.Add(b)
423
424            return id
425
426    def add_tool_old(self, tool):
427        """
428        Add a tool to the pallet.
429        """
430        bottonsize = (32, 32)
431        bottonborder = 10
432        toolbarborder = 1
433
434        if tool is None:
435            self.sizer.Add()
436            return None
437        else:
438            id = wx.NewId()
439            bitmap = tool.get_buttonbitmap()
440
441            # print '\n add_tool',key,bitmap
442            # print 'toolpallet.add_tool: key,name:',tool.key,tool.name
443            #b=GenBitmapTextToggleButton(self, id, bitmap,tool.name,name = tool.name)
444            b = GenBitmapToggleButton(self, id, bitmap, (bitmap.GetWidth() +
445                                                         bottonborder, bitmap.GetHeight()+bottonborder))
446            #b=GenBitmapToggleButton(self, wx.ID_DELETE)
447            #b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45))
448            self.Bind(wx.EVT_BUTTON, self.on_select, b)
449
450            if bitmap:
451                #mask = wx.Mask(bitmap, wx.BLUE)
452                # bitmap.SetMask(mask)
453                b.SetBitmapLabel(bitmap)
454                # bmp=wx.NullBitmap
455
456            bitmap_sel = tool.get_buttonbitmap_sel()
457            if bitmap_sel:
458                #mask = wx.Mask(bmp, wx.BLUE)
459                # bmp.SetMask(mask)
460                b.SetBitmapSelected(bitmap_sel)
461
462            b.SetUseFocusIndicator(False)
463            # b.SetSize((36,140))
464            # b.SetBestSize()
465            tt = wx.ToolTip(tool.get_info())
466            b.SetToolTip(tt)  # .SetTip(tool.tooltip)
467
468            self._id_to_tool[id] = (tool, b)
469
470            #self.sizer.Add(b, 0, wx.GROW)
471
472            self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder)
473            # self.sizer.Add(b)
474
475            return id
476
477    def get_tools(self):
478        """
479        Returns lins with all toll instances
480        """
481        tools = []
482        for (tool, b) in self._id_to_tool.values():
483            tools.append(tool)
484        return tools
485
486    def refresh(self):
487        """
488        Reorganizes toolpallet after adding/removing tools.
489        Attention is not automatically called.
490        """
491        self.sizer.Layout()
492
493    def on_select(self, event):
494
495        _id = event.GetEventObject().GetId()
496        print '\n on_select', _id, self._id  # ,self._id_to_tool[_id]
497        if _id != self._id:
498            if self._id_to_tool.has_key(_id):
499
500                (tool, button) = self._id_to_tool[_id]
501                print '  new tool', tool.get_name()
502                self.unselect()
503                self._id = _id
504                self.GetParent().set_options(tool)
505                if self._callback is not None:
506                    self._callback(tool)
507
508    def unselect(self):
509        """
510        Unselect currently selected tool.
511        """
512        if self._id_to_tool.has_key(self._id):
513            (tool, button) = self._id_to_tool[self._id]
514            if hasattr(button, 'SetToggle'):
515                button.SetToggle(False)
516            else:
517                # button.SetFocus()
518                # print 'button.SetFocus',button.SetFocus.__doc__
519                pass
520
521    def select(self, id):
522        """
523        Select explicitelt a tool.
524        """
525        print '\n select', id, self._id, self._id_to_tool
526
527        if id != self._id:
528            if self._id_to_tool.has_key(id):
529                # unselect previous
530                self.unselect()
531
532                # select and activate new tool
533                (tool, button) = self._id_to_tool[id]
534                button.SetToggle(True)
535                self._id = id
536                if self._callback is not None:
537                    self._callback(tool)
538
539
540class __ToggleMixin:
541    def SetToggle(self, flag):
542        self.up = not flag
543        self.Refresh()
544    SetValue = SetToggle
545
546    def GetToggle(self):
547        return not self.up
548    GetValue = GetToggle
549
550    def OnLeftDown(self, event):
551        if not self.IsEnabled():
552            return
553        self.saveUp = self.up
554        self.up = False  # not self.up
555        self.CaptureMouse()
556        self.SetFocus()
557        self.Refresh()
558
559    def OnLeftUp(self, event):
560        if not self.IsEnabled() or not self.HasCapture():
561            return
562        if self.HasCapture():
563            if self.up != self.saveUp:
564                self.Notify()
565            self.ReleaseMouse()
566            self.Refresh()
567
568    def OnKeyDown(self, event):
569        event.Skip()
570
571
572class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
573    """A generic toggle bitmap button with text label"""
574    pass
575
576
577class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
578    """A generic toggle bitmap button with text label"""
579    pass
580
581
582class ToolsPanel(wx.Panel):
583    """
584
585    Interactively navigates through objects and displays attributes
586    on a panel.
587    """
588
589    def __init__(self, parent):
590        wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize)
591
592        sizer = wx.BoxSizer(wx.VERTICAL)
593
594        self._toolspalett = ToolPalett(self)
595
596        # self._toolspalett.add_tool(BaseTool(self))
597
598        # create initial option panel
599        self._optionspanel = wx.Window(self)
600        self._optionspanel.SetBackgroundColour("pink")
601        wx.StaticText(self._optionspanel, -1, "Tool Options", (300, -1))
602
603        sizer.Add(self._toolspalett, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
604        sizer.Add(self._optionspanel, 1, wx.GROW)
605
606        # finish panel setup
607        self.SetSizer(sizer)
608        sizer.Fit(self)
609
610        # self.SetSize(parent.GetSize())
611
612    def add_tool(self, tool):
613        self._toolspalett.add_tool(tool)
614
615    def set_options(self, tool):
616        #self._optionspanel.change_obj(tool,groupnames = ['options'])
617        # self._optionspanel.change_obj(tool)
618        sizer = self.GetSizer()
619        sizer.Remove(1)
620        self._optionspanel.Destroy()
621
622        self._optionspanel = objpanel.ObjPanel(self, obj=tool,
623                                               attrconfigs=None,
624                                               #tables = None,
625                                               # table = None, id=None, ids=None,
626                                               groupnames=None,
627                                               func_change_obj=None,
628                                               show_groupnames=False, show_title=True, is_modal=False,
629                                               mainframe=None,
630                                               pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.MAXIMIZE_BOX | wx.RESIZE_BORDER,
631                                               immediate_apply=False, panelstyle='default',
632                                               standartbuttons=['apply', 'restore'])
633        # if id is not None:
634        #    self.objpanel=ObjPanel(self,obj,id=id,func_change_obj=self.change_obj)
635        # else:
636        #    self.objpanel=ObjPanel(self,obj,func_change_obj=self.change_obj)
637        sizer.Add(self._optionspanel, 1, wx.GROW)
638
639        self.Refresh()
640        # sizer.Fit(self)
641        sizer.Layout()
642
643
644def get_dist_point_to_segs(p, y1, x1, y2, x2, is_ending=True):
645    """
646    Minimum Distance between a Point p = (x,y) and a Line segments ,
647    where vectors x1, y1 are the first  points and x2,y2 are the second points
648    of the line segments.
649    Inspired by the description by Paul Bourke,    October 1988
650    http://paulbourke.net/geometry/pointlineplane/
651
652    Rewritten in vectorial form by Joerg Schweizer
653    """
654
655    y3, x3 = p
656
657    d = np.zeros(len(y1), dtype=np.float32)
658
659    dx21 = (x2-x1)
660    dy21 = (y2-y1)
661
662    lensq21 = dx21*dx21 + dy21*dy21
663
664    # indexvector for all zero length lines
665    iz = (lensq21 == 0)
666
667    dy = y3-y1[iz]
668    dx = x3-x1[iz]
669
670    d[iz] = dx*dx + dy*dy
671
672    lensq21[iz] = 1.0  # replace zeros with 1.0 to avoid div by zero error
673
674    u = (x3-x1)*dx21 + (y3-y1)*dy21
675    u = u / lensq21
676
677    x = x1 + u * dx21
678    y = y1 + u * dy21
679
680    if is_ending:
681        ie = u < 0
682        x[ie] = x1[ie]
683        y[ie] = y1[ie]
684        ie = u > 1
685        x[ie] = x2[ie]
686        y[ie] = y2[ie]
687
688    dx30 = x3-x
689    dy30 = y3-y
690    d[~iz] = (dx30*dx30 + dy30*dy30)[~iz]
691    return d
692
693
694def is_inside_triangles(p, x1, y1, x2, y2, x3, y3):
695    """
696    Returns a binary vector with True if point p is
697    inside a triangle.
698    x1,y1,x2,y2,x3,y3 are vectors with the 3 coordiantes of the triangles.
699    """
700    alpha = ((y2 - y3)*(p[0] - x3) + (x3 - x2)*(p[1] - y3)) \
701        / ((y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3))
702
703    beta = ((y3 - y1)*(p[0] - x3) + (x1 - x3)*(p[1] - y3)) \
704        / ((y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3))
705
706    gamma = 1.0 - alpha - beta
707    return (alpha > 0) & (beta > 0) & (gamma > 0)
708
709
710class WxGLTest_orig(glcanvas.GLCanvas):
711    def __init__(self, parent):
712
713        glcanvas.GLCanvas.__init__(self, parent, -1, attribList=[glcanvas.WX_GL_DOUBLEBUFFER])
714        wx.EVT_PAINT(self, self.OnDraw)
715        wx.EVT_SIZE(self, self.OnSize)
716        wx.EVT_MOTION(self, self.OnMouseMotion)
717        wx.EVT_WINDOW_DESTROY(self, self.OnDestroy)
718
719        self.init = True
720
721    def OnDraw(self, event):
722        self.SetCurrent()
723
724        if not self.init:
725            self.InitGL()
726            self.init = False
727
728        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
729        glLoadIdentity()
730
731        # Draw the spiral in 'immediate mode'
732        # WARNING: You should not be doing the spiral calculation inside the loop
733        # even if you are using glBegin/glEnd, sin/cos are fairly expensive functions
734        # I've left it here as is to make the code simpler.
735        radius = 1.0
736        x = radius*math.sin(0)
737        y = radius*math.cos(0)
738        glColor(0.0, 1.0, 0.0)
739        glBegin(GL_LINE_STRIP)
740        for deg in xrange(1000):
741            glVertex(x, y, 0.0)
742            rad = math.radians(deg)
743            radius -= 0.001
744            x = radius*math.sin(rad)
745            y = radius*math.cos(rad)
746        glEnd()
747
748        glEnableClientState(GL_VERTEX_ARRAY)
749
750        spiral_array = []
751
752        # Second Spiral using "array immediate mode" (i.e. Vertex Arrays)
753        radius = 0.8
754        x = radius*math.sin(0)
755        y = radius*math.cos(0)
756        glColor(1.0, 0.0, 0.0)
757        for deg in xrange(820):
758            spiral_array.append([x, y])
759            rad = math.radians(deg)
760            radius -= 0.001
761            x = radius*math.sin(rad)
762            y = radius*math.cos(rad)
763
764        glVertexPointerf(spiral_array)
765        glDrawArrays(GL_LINE_STRIP, 0, len(spiral_array))
766        glFlush()
767        self.SwapBuffers()
768        return
769
770    def InitGL(self):
771        '''
772        Initialize GL
773        '''
774
775#        # set viewing projection
776#        glClearColor(0.0, 0.0, 0.0, 1.0)
777#        glClearDepth(1.0)
778#
779#        glMatrixMode(GL_PROJECTION)
780#        glLoadIdentity()
781#        gluPerspective(40.0, 1.0, 1.0, 30.0)
782#
783#        glMatrixMode(GL_MODELVIEW)
784#        glLoadIdentity()
785#        gluLookAt(0.0, 0.0, 10.0,
786#                  0.0, 0.0, 0.0,
787#                  0.0, 1.0, 0.0)
788
789    def OnSize(self, event):
790
791        try:
792            width, height = event.GetSize()
793        except:
794            width = event.GetSize().width
795            height = event.GetSize().height
796
797        self.Refresh()
798        self.Update()
799
800    def OnMouseMotion(self, event):
801        x = event.GetX()
802        y = event.GetY()
803
804    def OnDestroy(self, event):
805        print "Destroying Window"
806
807
808class Lines:
809    """Lines class."""
810
811    def __init__(self, linewidth=3, vertices=None, colors=None):
812        self.name = 'Lines'
813        self.n_vert_per_elem = 2
814        self.linewidth = linewidth
815        self.c_highl = 0.3
816        self.detectwidth = 0.1  # m
817        self.set_attrs(vertices, colors)
818
819    def set_attrs(self, vertices, colors):
820
821        self.vertices = np.array(vertices, dtype=np.float32)
822        self._update_vertexvbo()
823
824        self.colors = np.array(colors, dtype=np.float32)
825        self.colors_highl = np.zeros((len(colors), 4), dtype=np.float32)
826        self._update_colorvbo()
827
828    def _update_vertexvbo(self):
829        self._vertexvbo = vbo.VBO(self.vertices.reshape((-1, 3)))
830        self._indexvbo = vbo.VBO(np.arange(self.n_vert_per_elem*len(self.vertices),
831                                           dtype=np.int32), target=GL_ELEMENT_ARRAY_BUFFER)
832
833    def _update_colorvbo(self):
834        #self._colorvbo = vbo.VBO( np.resize( np.repeat(np.clip((self.colors+self.c_highl*self.colors_highl) ,0.0,1.0), self.n_vert_per_elem),(len(self.colors),4)) )
835        self._colorvbo = vbo.VBO(np.clip((self.colors+self.colors_highl)
836                                         [np.array(np.arange(0, len(self.colors), 1.0/self.n_vert_per_elem), int)], 0.0, 1.0))
837
838    def pick(self, p):
839        """
840        Returns a binary vector which is True values for lines that have been selected
841        by point p.
842
843        In particular, an element of this vector is True if the minimum distance
844        between the respective line to point p is less than self.detectwidth
845        """
846        x1 = self.vertices[:, 0, 0]
847        y1 = self.vertices[:, 0, 1]
848
849        x2 = self.vertices[:, 1, 0]
850        y2 = self.vertices[:, 1, 1]
851
852        return get_dist_point_to_segs(p, x1, y1, x2, y2, is_ending=True) < self.detectwidth**2
853
854    def highlight(self, inds_highl):
855        self.colors_highl = np.repeat(self.c_highl*np.array(inds_highl, dtype=np.float32), 4).reshape(-1, 4)
856        self._update_colorvbo()
857
858    def draw(self):
859        glLineWidth(self.linewidth)
860
861        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
862        glEnable(GL_BLEND)
863
864        glEnableClientState(GL_VERTEX_ARRAY)
865        glEnableClientState(GL_COLOR_ARRAY)
866
867        self._colorvbo.bind()
868        glColorPointer(4, GL_FLOAT, 0, None)
869
870        self._vertexvbo.bind()
871        self._indexvbo.bind()
872        glVertexPointer(3, GL_FLOAT, 0, None)
873
874        glDrawElements(GL_LINES, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None)
875
876        glDisableClientState(GL_VERTEX_ARRAY)
877        glDisableClientState(GL_COLOR_ARRAY)
878        self._vertexvbo.unbind()
879        self._indexvbo.unbind()
880        self._colorvbo.unbind()
881
882
883class Rectangles(Lines):
884
885    def __init__(self, linewidth=3, vertices=None, colors=None):
886        self.name = 'Rectangles'
887        self.n_vert_per_elem = 4
888        self.c_highl = 0.3
889        self.detectwidth = 0.1  # m
890        self.linewidth = linewidth
891
892        self.set_attrs(vertices, colors)
893
894    def pick(self, p):
895
896        x1 = self.vertices[:, 0, 0]
897        y1 = self.vertices[:, 0, 1]
898
899        x2 = self.vertices[:, 1, 0]
900        y2 = self.vertices[:, 1, 1]
901
902        x3 = self.vertices[:, 2, 0]
903        y3 = self.vertices[:, 2, 1]
904
905        x4 = self.vertices[:, 3, 0]
906        y4 = self.vertices[:, 3, 1]
907
908        return is_inside_triangles(p, x1, y1, x2, y2, x3, y3) | is_inside_triangles(p, x1, y1, x3, y3, x4, y4)
909
910    def draw(self):
911        glLineWidth(self.linewidth)
912
913        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
914        glEnable(GL_BLEND)
915
916        glEnableClientState(GL_VERTEX_ARRAY)
917        glEnableClientState(GL_COLOR_ARRAY)
918
919        self._colorvbo.bind()
920        glColorPointer(4, GL_FLOAT, 0, None)
921
922        self._vertexvbo.bind()
923        self._indexvbo.bind()
924        glVertexPointer(3, GL_FLOAT, 0, None)
925
926        glDrawElements(GL_QUADS, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None)
927
928        glDisableClientState(GL_VERTEX_ARRAY)
929        glDisableClientState(GL_COLOR_ARRAY)
930        self._vertexvbo.unbind()
931        self._indexvbo.unbind()
932        self._colorvbo.unbind()
933
934
935class Triangles(Lines):
936    """Triangles class."""
937
938    def __init__(self, linewidth=3, vertices=None, colors=None):
939        self.name = 'Triangles'  # ,self.__name__
940        self.n_vert_per_elem = 3
941        self.c_highl = 0.3
942        self.detectwidth = 0.1  # m
943        self.linewidth = linewidth
944
945        self.set_attrs(vertices, colors)
946
947    def pick(self, p):
948        return is_inside_triangles(p, self.vertices[:, 0, 0], self.vertices[:, 0, 1], self.vertices[:, 1, 0], self.vertices[:, 1, 1], self.vertices[:, 2, 0], self.vertices[:, 2, 1])
949
950    def draw(self):
951        glLineWidth(self.linewidth)
952
953        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
954        glEnable(GL_BLEND)
955
956        glEnableClientState(GL_VERTEX_ARRAY)
957        glEnableClientState(GL_COLOR_ARRAY)
958
959        self._colorvbo.bind()
960        glColorPointer(4, GL_FLOAT, 0, None)
961
962        self._vertexvbo.bind()
963        self._indexvbo.bind()
964        glVertexPointer(3, GL_FLOAT, 0, None)
965
966        glDrawElements(GL_TRIANGLES, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None)
967
968        glDisableClientState(GL_VERTEX_ARRAY)
969        glDisableClientState(GL_COLOR_ARRAY)
970        self._vertexvbo.unbind()
971        self._indexvbo.unbind()
972        self._colorvbo.unbind()
973
974
975class GLFrame(wx.Frame):
976    """A simple class for using OpenGL with wxPython."""
977
978    def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition,
979                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
980                 name='frame', mainframe=None):
981
982        print '\n\nGLFrame!!'
983        if mainframe is None:
984            self._mainframe = parent
985        else:
986            self._mainframe = mainframe
987
988        self._elements = []
989        self.elements_selected = []
990
991        self.eyex = 0.0
992        self.eyey = 0.0
993        self.eyez = -9.0
994
995        self.centerx = 0.0
996        self.centery = 0.0
997        self.centerz = 0.0
998
999        self.upx = -1.0
1000        self.upy = 0.0
1001        self.upz = 0.0
1002
1003        self.g_Width = 600
1004        self.g_Height = 600
1005
1006        self.g_nearPlane = 1.
1007        self.g_farPlane = 1000.
1008
1009        self.action = ""
1010        self.xStart = self.yStart = 0.
1011        self.xStart
1012        self.zoom = 65.
1013
1014        self.xRotate = 0.
1015        self.yRotate = 0.
1016        self.zRotate = 0.
1017
1018        self.xTrans = 0.
1019        self.yTrans = 0.
1020
1021        #
1022        # Forcing a specific style on the window.
1023        #   Should this include styles passed?
1024        style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1025
1026        super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)
1027        #wx.Frame.__init__(self, parent, id, title, pos, size, style, name)
1028
1029        self.GLinitialized = False
1030        attribList = (glcanvas.WX_GL_RGBA,  # RGBA
1031                      glcanvas.WX_GL_DOUBLEBUFFER,  # Double Buffered
1032                      glcanvas.WX_GL_DEPTH_SIZE, 24)  # 24 bit
1033
1034        #
1035        # Create the canvas
1036        self.canvas = glcanvas.GLCanvas(self, attribList=attribList)
1037
1038        #
1039        # Set the event handlers.
1040        self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
1041        self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
1042        self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)
1043
1044        self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
1045        self.canvas.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
1046        self.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
1047        self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel)
1048
1049        self.Show()
1050        # this is needed to initialize GL projections for unproject
1051        wx.CallAfter(self.processSizeEvent)
1052
1053    def add_element(self, element):
1054        self._elements.append(element)
1055        self.OnDraw()
1056
1057    def resetView():
1058        self.zoom = 65.
1059        self.xRotate = 0.
1060        self.yRotate = 0.
1061        self.zRotate = 0.
1062        self.xTrans = 0.
1063        self.yTrans = 0.
1064        self.OnDraw()
1065
1066    def OnWheel(self, event):
1067        #EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
1068        #
1069        Rot = event.GetWheelRotation()
1070        # print 'OnWheel!!',Rot,event.ControlDown(),event.ShiftDown()
1071        if event.ControlDown():  # event.ControlDown(): # zoom
1072            if Rot < 0:
1073                self.zoom *= 0.9
1074            else:
1075                self.zoom *= 1.1
1076            self.OnDraw()
1077            event.Skip()
1078
1079    def OnLeftDown(self, event):
1080        ##
1081        if (event.ControlDown() & event.ShiftDown()) & (self.action == ''):
1082            self.action = 'drag'
1083            self.BeginGrap(event)
1084            event.Skip()
1085
1086    def OnLeftUp(self, event):
1087        if self.action == 'drag':
1088            self.EndGrap(event)
1089            self.action == ''
1090            event.Skip()
1091
1092    def get_intersection(self, v_near, v_far):
1093        # 150918
1094        # idea from http://www.bfilipek.com/2012/06/select-mouse-opengl.html
1095        # https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection
1096        d = -v_near + v_far
1097
1098        t = -v_near[2]/d[2]
1099        v_inter = v_near+t*d
1100
1101        return v_inter
1102
1103    def OnMotion(self, event):
1104
1105        p = self.unproject(event)[0:2]
1106        for element in self._elements:
1107            inds_pick = element.pick(p)
1108            element.highlight(inds_pick)
1109
1110        self.OnDraw()
1111
1112        if (event.ControlDown() & event.ShiftDown() & (self.action == 'drag')):
1113            self.MoveGrap(event)
1114            self.OnDraw()
1115            event.Skip()
1116
1117        elif (self.action == 'drag'):
1118            self.EndGrap(event)
1119            self.action == ''
1120            event.Skip()
1121
1122    def unproject(self, event):
1123        """Get the world coordinates for viewCoordinate for the event
1124        """
1125        mousex, mousey = event.GetPosition()
1126        x = mousex
1127        y = self.g_Height-mousey
1128
1129        modelviewmatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
1130        projectionmatrix = glGetDoublev(GL_PROJECTION_MATRIX)
1131        viewport = glGetInteger(GL_VIEWPORT)
1132        z = 0.0
1133        worldCoordinate_near = np.array(gluUnProject(
1134            x, y, z,
1135            modelviewmatrix,
1136            projectionmatrix,
1137            viewport,), dtype=np.float32)
1138        z = 1.0
1139        worldCoordinate_far = np.array(gluUnProject(
1140            x, y, z,
1141            modelviewmatrix,
1142            projectionmatrix,
1143            viewport,), dtype=np.float32)
1144
1145        v_inter = self.get_intersection(worldCoordinate_near, worldCoordinate_far)
1146        return v_inter
1147
1148    def BeginGrap(self, event):
1149
1150        self.xStart, self.yStart = event.GetPosition()
1151        # print 'BeginGrap',self.xStart,self.yStart
1152
1153    def MoveGrap(self, event):
1154        x, y = event.GetPosition()
1155
1156        self.xTrans += x-self.xStart
1157        self.yTrans += y-self.yStart
1158        # print 'MoveGrap',self.xTrans,self.yTrans
1159        self.xStart, self.yStart = x, y
1160
1161    def EndGrap(self, event):
1162        # print 'EndGrap'
1163        self.canvas.SetCursor(wx.NullCursor)
1164        self.action = ''
1165
1166    #
1167    # Canvas Proxy Methods
1168
1169    def GetGLExtents(self):
1170        """Get the extents of the OpenGL canvas."""
1171        return self.canvas.GetClientSize()
1172
1173    def SwapBuffers(self):
1174        """Swap the OpenGL buffers."""
1175        self.canvas.SwapBuffers()
1176
1177    #
1178    # wxPython Window Handlers
1179
1180    def processEraseBackgroundEvent(self, event):
1181        """Process the erase background event."""
1182        pass  # Do nothing, to avoid flashing on MSWin
1183
1184    def processSizeEvent(self, event=None):
1185        """Process the resize event."""
1186        if self.canvas.GetContext():
1187            # Make sure the frame is shown before calling SetCurrent.
1188            self.Show()
1189            self.canvas.SetCurrent()
1190
1191            size = self.GetGLExtents()
1192            self.OnReshape(size.width, size.height)
1193            self.canvas.Refresh(False)
1194        if event:
1195            event.Skip()
1196
1197    def processPaintEvent(self, event):
1198        """Process the drawing event."""
1199        self.canvas.SetCurrent()
1200
1201        # This is a 'perfect' time to initialize OpenGL ... only if we need to
1202        if not self.GLinitialized:
1203            self.OnInitGL()
1204            self.GLinitialized = True
1205
1206        self.OnDraw()
1207        event.Skip()
1208
1209    #
1210    # GLFrame OpenGL Event Handlers
1211
1212    def OnInitGL(self):
1213        """Initialize OpenGL for use in the window."""
1214        glClearColor(0, 0, 0, 1)
1215
1216    def OnReshape(self, width, height):
1217        """Reshape the OpenGL viewport based on the dimensions of the window."""
1218        #global g_Width, g_Height
1219        self.g_Width = width
1220        self.g_Height = height
1221        glViewport(0, 0, self.g_Width, self.g_Height)
1222
1223    def OnDraw(self, *args, **kwargs):
1224        """Draw the window."""
1225        # Clear frame buffer and depth buffer
1226        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
1227        # Set up viewing transformation, looking down -Z axis
1228        glLoadIdentity()
1229        gluLookAt(self.eyex, self.eyey, self.eyez, self.centerx, self.centery,
1230                  self.centerz, self.upx, self.upy, self.upz)  # -.1,0,0
1231
1232        # Set perspective (also zoom)
1233        glMatrixMode(GL_PROJECTION)
1234        glLoadIdentity()
1235        # the window corner OpenGL coordinates are (-+1, -+1)
1236        glOrtho(-1, 1, 1, -1, -1, 1)
1237
1238        aspect = float(self.g_Width)/float(self.g_Height)
1239
1240        gluPerspective(self.zoom, aspect, self.g_nearPlane, self.g_farPlane)
1241        glMatrixMode(GL_MODELVIEW)
1242        self.polarView()
1243
1244        # draw actual scene
1245        for element in self._elements:
1246            element.draw()
1247
1248        self.SwapBuffers()
1249
1250    def polarView(self):
1251        glTranslatef(self.yTrans/100., 0.0, 0.0)
1252        glTranslatef(0.0, -self.xTrans/100., 0.0)
1253        glRotatef(-self.zRotate, 0.0, 0.0, 1.0)
1254        glRotatef(-self.xRotate, 1.0, 0.0, 0.0)
1255        glRotatef(-self.yRotate, .0, 1.0, 0.0)
1256
1257
1258class WxGLTest2(glcanvas.GLCanvas):
1259    def __init__(self, parent, mainframe=None):
1260        if mainframe is None:
1261            self._mainframe = parent
1262        else:
1263            self._mainframe = mainframe
1264
1265        self._elements = []
1266        self.elements_selected = []
1267
1268        self.eyex = 0.0
1269        self.eyey = 0.0
1270        self.eyez = -9.0
1271
1272        self.centerx = 0.0
1273        self.centery = 0.0
1274        self.centerz = 0.0
1275
1276        self.upx = -1.0
1277        self.upy = 0.0
1278        self.upz = 0.0
1279
1280        self.g_Width = 600
1281        self.g_Height = 600
1282
1283        self.g_nearPlane = 1.
1284        self.g_farPlane = 1000.
1285
1286        self.action = ""
1287        self.xStart = self.yStart = 0.
1288        self.xStart
1289        self.zoom = 65.
1290
1291        self.xRotate = 0.
1292        self.yRotate = 0.
1293        self.zRotate = 0.
1294
1295        self.xTrans = 0.
1296        self.yTrans = 0.
1297
1298        #
1299        # Forcing a specific style on the window.
1300        #   Should this include styles passed?
1301        style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1302
1303        attribList = (glcanvas.WX_GL_RGBA,  # RGBA
1304                      glcanvas.WX_GL_DOUBLEBUFFER,  # Double Buffered
1305                      glcanvas.WX_GL_DEPTH_SIZE, 24)  # 24 bit
1306
1307        glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList)
1308        #super(WxGLTest2, self).__init__(parent,-1, attribList=attribList)
1309
1310        self.GLinitialized = False
1311
1312        ###
1313        # Forcing a specific style on the window.
1314        #   Should this include styles passed?
1315        #style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1316
1317        #super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)
1318        #wx.Frame.__init__(self, parent, id, title, pos, size, style, name)
1319
1320        #
1321        # Set the event handlers.
1322        self.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
1323        self.Bind(wx.EVT_SIZE, self.OnSize)
1324        self.Bind(wx.EVT_PAINT, self.processPaintEvent)
1325
1326        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
1327        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
1328        self.Bind(wx.EVT_MOTION, self.OnMotion)
1329        self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel)
1330
1331        # this is needed to initialize GL projections for unproject
1332        wx.CallAfter(self.OnSize)
1333
1334    def add_element(self, element):
1335        self._elements.append(element)
1336        self.OnDraw()
1337
1338    def resetView():
1339        self.zoom = 65.
1340        self.xRotate = 0.
1341        self.yRotate = 0.
1342        self.zRotate = 0.
1343        self.xTrans = 0.
1344        self.yTrans = 0.
1345        self.OnDraw()
1346
1347    def OnWheel(self, event):
1348        #EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
1349        #
1350        Rot = event.GetWheelRotation()
1351        # print 'OnWheel!!',Rot,event.ControlDown(),event.ShiftDown()
1352        if event.ControlDown():  # event.ControlDown(): # zoom
1353            if Rot < 0:
1354                self.zoom *= 0.9
1355            else:
1356                self.zoom *= 1.1
1357            self.OnDraw()
1358            event.Skip()
1359
1360    def OnLeftDown(self, event):
1361        ##
1362        if (event.ControlDown() & event.ShiftDown()) & (self.action == ''):
1363            self.action = 'drag'
1364            self.BeginGrap(event)
1365            event.Skip()
1366
1367    def OnLeftUp(self, event):
1368        if self.action == 'drag':
1369            self.EndGrap(event)
1370            self.action == ''
1371            event.Skip()
1372
1373    def get_intersection(self, v_near, v_far):
1374        # 150918
1375        # idea from http://www.bfilipek.com/2012/06/select-mouse-opengl.html
1376        # https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection
1377        d = -v_near + v_far
1378
1379        t = -v_near[2]/d[2]
1380        v_inter = v_near+t*d
1381
1382        return v_inter
1383
1384    def OnMotion(self, event):
1385
1386        p = self.unproject(event)[0:2]
1387        for element in self._elements:
1388            inds_pick = element.pick(p)
1389            element.highlight(inds_pick)
1390
1391        self.OnDraw()
1392
1393        if (event.ControlDown() & event.ShiftDown() & (self.action == 'drag')):
1394            self.MoveGrap(event)
1395            self.OnDraw()
1396            event.Skip()
1397
1398        elif (self.action == 'drag'):
1399            self.EndGrap(event)
1400            self.action == ''
1401            event.Skip()
1402
1403    def unproject(self, event):
1404        """Get the world coordinates for viewCoordinate for the event
1405        """
1406        mousex, mousey = event.GetPosition()
1407        x = mousex
1408        y = self.g_Height-mousey
1409
1410        modelviewmatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
1411        projectionmatrix = glGetDoublev(GL_PROJECTION_MATRIX)
1412        viewport = glGetInteger(GL_VIEWPORT)
1413        z = 0.0
1414        worldCoordinate_near = np.array(gluUnProject(
1415            x, y, z,
1416            modelviewmatrix,
1417            projectionmatrix,
1418            viewport,), dtype=np.float32)
1419        z = 1.0
1420        worldCoordinate_far = np.array(gluUnProject(
1421            x, y, z,
1422            modelviewmatrix,
1423            projectionmatrix,
1424            viewport,), dtype=np.float32)
1425
1426        v_inter = self.get_intersection(worldCoordinate_near, worldCoordinate_far)
1427        return v_inter
1428
1429    def BeginGrap(self, event):
1430
1431        self.xStart, self.yStart = event.GetPosition()
1432        # print 'BeginGrap',self.xStart,self.yStart
1433
1434    def MoveGrap(self, event):
1435        x, y = event.GetPosition()
1436
1437        self.xTrans += x-self.xStart
1438        self.yTrans += y-self.yStart
1439        # print 'MoveGrap',self.xTrans,self.yTrans
1440        self.xStart, self.yStart = x, y
1441
1442    def EndGrap(self, event):
1443        # print 'EndGrap'
1444        self.SetCursor(wx.NullCursor)
1445        self.action = ''
1446
1447    #
1448    # Canvas Proxy Methods
1449
1450    def GetGLExtents(self):
1451        """Get the extents of the OpenGL canvas."""
1452        return self.GetClientSize()
1453
1454    # def SwapBuffers(self):
1455    #    """Swap the OpenGL buffers."""
1456    #    self.SwapBuffers()
1457
1458    #
1459    # wxPython Window Handlers
1460
1461    def processEraseBackgroundEvent(self, event):
1462        """Process the erase background event."""
1463        pass  # Do nothing, to avoid flashing on MSWin
1464
1465    def OnSize(self, event=None, win=None):
1466        """Process the resize event."""
1467        if self.GetContext():
1468            # Make sure the frame is shown before calling SetCurrent.
1469            self.Show()
1470            self.SetCurrent()
1471
1472            size = self.GetGLExtents()
1473            self.OnReshape(size.width, size.height)
1474            self.Refresh(False)
1475        if event:
1476            event.Skip()
1477
1478    def processPaintEvent(self, event):
1479        """Process the drawing event."""
1480        self.SetCurrent()
1481
1482        # This is a 'perfect' time to initialize OpenGL ... only if we need to
1483        if not self.GLinitialized:
1484            self.OnInitGL()
1485            self.GLinitialized = True
1486
1487        self.OnDraw()
1488        event.Skip()
1489
1490    #
1491    # GLFrame OpenGL Event Handlers
1492
1493    def OnInitGL(self):
1494        """Initialize OpenGL for use in the window."""
1495        glClearColor(0, 0, 0, 1)
1496
1497    def OnReshape(self, width, height):
1498        """Reshape the OpenGL viewport based on the dimensions of the window."""
1499        #global g_Width, g_Height
1500        self.g_Width = width
1501        self.g_Height = height
1502        glViewport(0, 0, self.g_Width, self.g_Height)
1503
1504    def OnDraw(self, *args, **kwargs):
1505        """Draw the window."""
1506        # Clear frame buffer and depth buffer
1507        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
1508        # Set up viewing transformation, looking down -Z axis
1509        glLoadIdentity()
1510        gluLookAt(self.eyex, self.eyey, self.eyez, self.centerx, self.centery,
1511                  self.centerz, self.upx, self.upy, self.upz)  # -.1,0,0
1512
1513        # Set perspective (also zoom)
1514        glMatrixMode(GL_PROJECTION)
1515        glLoadIdentity()
1516        # the window corner OpenGL coordinates are (-+1, -+1)
1517        glOrtho(-1, 1, 1, -1, -1, 1)
1518
1519        aspect = float(self.g_Width)/float(self.g_Height)
1520
1521        gluPerspective(self.zoom, aspect, self.g_nearPlane, self.g_farPlane)
1522        glMatrixMode(GL_MODELVIEW)
1523        self.polarView()
1524
1525        # draw actual scene
1526        for element in self._elements:
1527            element.draw()
1528            # causes bad things :AttributeError: 'Implementation' object has no attribute 'glGenBuffers'
1529
1530        self.SwapBuffers()
1531
1532    def polarView(self):
1533        glTranslatef(self.yTrans/100., 0.0, 0.0)
1534        glTranslatef(0.0, -self.xTrans/100., 0.0)
1535        glRotatef(-self.zRotate, 0.0, 0.0, 1.0)
1536        glRotatef(-self.xRotate, 1.0, 0.0, 0.0)
1537        glRotatef(-self.yRotate, .0, 1.0, 0.0)
1538
1539
1540class GlEditorSash(wx.SplitterWindow):
1541
1542    def __init__(self, parent,
1543                 mainframe=None,
1544                 size=wx.DefaultSize,
1545                 is_menu=False,  # create menu items
1546                 Debug=0,
1547                 ):
1548
1549        wx.SplitterWindow.__init__(self, parent, wx.ID_ANY,
1550                                   style=wx.SP_LIVE_UPDATE,
1551                                   size=size)
1552        self.log = log
1553
1554        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
1555        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
1556
1557        # id=wx.ID_ANY
1558        # pixel_snap=10 # radius in pixels in which a point is selected
1559        # n_test=5
1560        # wx.Window.__init__(self,parent,id,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS)
1561        # wx.Panel.__init__(self,parent,id,wx.DefaultPosition,size,wx.SUNKEN_BORDER|wx.WANTS_CHARS)
1562        # self.parent=parent
1563        self._mainframe = mainframe  # mainframe
1564        #splitter = Splitter(self)
1565
1566        self._toolspanel = ToolPalett(self)
1567        self._toolspanel.add_tool(BaseTool(self))
1568        #sty = wx.BORDER_SUNKEN
1569        #self._toolspanel = wx.Window(self, style=sty)
1570        # self._toolspanel.SetBackgroundColour("pink")
1571        #wx.StaticText(self._toolspanel, -1, "Object", (50,50))
1572
1573        self._canvas = WxGLTest2(self)
1574        #p2 = wx.Window(self, style=sty)
1575        # p2.SetBackgroundColour("blue")
1576        #wx.StaticText(p2, -1, "GLeditor", (50,50))
1577
1578        #self.canvas = wx.Window(splitter, style=sty)
1579        # self.canvas.SetBackgroundColour("green")
1580        #wx.StaticText(self.canvas, -1, "Panel two", (50,50))
1581
1582        #self.canvas = WxGLTest2(splitter)
1583        #self.canvas = WxGLTest_orig(splitter)
1584
1585        #nbpanel = wx.Panel(splitter)
1586        #self._viewtabs = wx.Notebook(nbpanel,wx.ID_ANY, style=wx.CLIP_CHILDREN)
1587        #sizer = wx.BoxSizer(wx.VERTICAL)
1588        #sizer.Add(self._viewtabs, 1, wx.ALL|wx.EXPAND, 5)
1589        # nbpanel.SetSizer(sizer)
1590        # self.Layout()
1591
1592        # finally, put the notebook in a sizer for the panel to manage
1593        # the layout
1594        #sizer = wx.BoxSizer()
1595        #sizer.Add(self._viewtabs, 1, wx.EXPAND)
1596        # self.SetSizer(sizer)
1597
1598        self.SetMinimumPaneSize(20)
1599        #splitter.SplitVertically(p1, self.canvas, -100)
1600        self.SplitVertically(self._toolspanel, self._canvas, -100)
1601
1602        #wx.EVT_SIZE  (self, self.on_size)
1603        self.SetSashPosition(500, True)
1604        # sizer=wx.BoxSizer(wx.VERTICAL)
1605        # sizer.Add(p1,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)# from NaviPanelTest
1606        # sizer.Add(self.canvas,1,wx.GROW)# from NaviPanelTest
1607
1608        # finish panel setup
1609        # self.SetSizer(sizer)
1610        # sizer.Fit(self)
1611        # self.on_size()
1612        # self.Show()
1613
1614    def get_canvas(self):
1615        return self._canvas
1616
1617    def OnSashChanged(self, evt):
1618        #print("sash changed to %s\n" % str(evt.GetSashPosition()))
1619        pass
1620
1621    def OnSashChanging(self, evt):
1622        print("sash changing to %s\n" % str(evt.GetSashPosition()))
1623        # uncomment this to not allow the change
1624        # evt.SetSashPosition(-1)
1625        # evt.SetSashPosition(210)
1626        # self.canvas.OnSize()
1627        pass
1628
1629    def on_size(self, event=None):
1630        # self.tc.SetSize(self.GetSize())
1631        # self.tc.SetSize(self.GetSize())
1632        # self._viewtabs.SetSize(self.GetSize())
1633        # pass
1634        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1635        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1636
1637        # important:
1638        #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs)
1639
1640        if event:
1641            event.Skip()
1642
1643
1644class GlEditor(wx.Panel):
1645
1646    def __init__(self, parent,
1647                 mainframe=None,
1648                 size=wx.DefaultSize,
1649                 is_menu=False,  # create menu items
1650                 Debug=0,
1651                 ):
1652
1653        wx.Panel.__init__(self, parent, wx.ID_ANY, size=size)
1654        sizer = wx.BoxSizer(wx.HORIZONTAL)
1655
1656        self._mainframe = mainframe
1657
1658        self._toolspanel = ToolsPanel(self)
1659        for i in range(5):
1660            self._toolspanel.add_tool(BaseTool(self))
1661        self._toolspanel.add_tool(DelTool(self))
1662        #sty = wx.BORDER_SUNKEN
1663        #self._toolspanel = wx.Window(self, style=sty)
1664        # self._toolspanel.SetBackgroundColour("pink")
1665        #wx.StaticText(self._toolspanel, -1, "Object", (50,50))
1666
1667        self._canvas = WxGLTest2(self)
1668        #p2 = wx.Window(self, style=sty)
1669        # p2.SetBackgroundColour("blue")
1670        #wx.StaticText(p2, -1, "GLeditor", (50,50))
1671
1672        sizer.Add(self._toolspanel, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)  # from NaviPanelTest
1673        sizer.Add(self._canvas, 1, wx.GROW)  # from NaviPanelTest
1674
1675        # finish panel setup
1676        self.SetSizer(sizer)
1677        sizer.Fit(self)
1678
1679    def get_canvas(self):
1680        return self._canvas
1681
1682    def on_size(self, event=None):
1683        # self.tc.SetSize(self.GetSize())
1684        # self.tc.SetSize(self.GetSize())
1685        # self._viewtabs.SetSize(self.GetSize())
1686        # pass
1687        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1688        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1689
1690        # important:
1691        #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs)
1692
1693        if event:
1694            event.Skip()
1695
1696
1697class MainSplitter(wx.SplitterWindow):
1698    def __init__(self, parent, ID=-1):
1699        wx.SplitterWindow.__init__(self, parent, ID,
1700                                   style=wx.SP_LIVE_UPDATE
1701                                   )
1702
1703        self.SetMinimumPaneSize(20)
1704
1705        #sty = wx.BORDER_SUNKEN
1706
1707        emptyobj = cm.BaseObjman('empty')
1708        self._objbrowser = objpanel.NaviPanel(self,
1709                                              emptyobj,
1710                                              #show_title = False
1711                                              #size = w.DefaultSize,
1712                                              #style = wx.DEFAULT_DIALOG_STYLE|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER,
1713                                              # choose_id=False,choose_attr=False,
1714                                              # func_choose_id=None,
1715                                              # func_change_obj=None,
1716                                              #panelstyle = 'default',
1717                                              immediate_apply=False,
1718                                              buttons=[],
1719                                              standartbuttons=[],
1720                                              #defaultbutton = defaultbutton,
1721                                              )
1722
1723        #p1 = wx.Window(splitter, style=sty)
1724        # p1.SetBackgroundColour("pink")
1725        #wx.StaticText(p1, -1, "Object", (50,50))
1726
1727        #self.canvas = wx.Window(splitter, style=sty)
1728        # self.canvas.SetBackgroundColour("green")
1729        #wx.StaticText(self.canvas, -1, "Panel two", (50,50))
1730        #self.canvas = WxGLTest2(splitter)
1731        #self.canvas = WxGLTest_orig(splitter)
1732
1733        #self._viewtabs = wx.Notebook(self,wx.ID_ANY, style=wx.CLIP_CHILDREN)
1734        self._viewtabs = wx.Notebook(self, -1, size=(21, 21), style=wx.BK_DEFAULT
1735                                     # wx.BK_TOP
1736                                     # wx.BK_BOTTOM
1737                                     # wx.BK_LEFT
1738                                     # wx.BK_RIGHT
1739                                     # | wx.NB_MULTILINE
1740                                     )
1741
1742        #nbpanel = wx.Panel(splitter)
1743        #self._viewtabs = wx.Notebook(nbpanel,wx.ID_ANY, style=wx.CLIP_CHILDREN)
1744        #sizer = wx.BoxSizer(wx.VERTICAL)
1745        #sizer.Add(self._viewtabs, 1, wx.ALL|wx.EXPAND, 5)
1746        # nbpanel.SetSizer(sizer)
1747        # self.Layout()
1748
1749        # finally, put the notebook in a sizer for the panel to manage
1750        # the layout
1751        #sizer = wx.BoxSizer()
1752        #sizer.Add(self._viewtabs, 1, wx.EXPAND)
1753        # self.SetSizer(sizer)
1754
1755        #splitter.SplitVertically(self._objbrowser,self.canvas , -100)
1756        self.SplitVertically(self._objbrowser, self._viewtabs, -100)
1757
1758        self.SetSashPosition(500, True)
1759        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged)
1760        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging)
1761
1762    def add_view(self, name, ViewClass, **args):
1763        """
1764        Add a new view to the notebook.
1765        """
1766        # print 'context.add_view',ViewClass
1767        # print '  args',args
1768        view = ViewClass(self._viewtabs,
1769                         mainframe=self.GetParent(),
1770                         **args
1771                         )
1772
1773        # Add network tab with editor
1774        p = self._viewtabs.AddPage(view, name.title())
1775        #self._views[name] = view
1776        # self._viewtabs.SetSelection(p)
1777        # self._viewtabs.Show(True)
1778        return view
1779
1780    def OnSashChanged(self, evt):
1781        #print("sash changed to %s\n" % str(evt.GetSashPosition()))
1782        pass
1783
1784    def OnSashChanging(self, evt):
1785        #print("sash changing to %s\n" % str(evt.GetSashPosition()))
1786        # uncomment this to not allow the change
1787        # evt.SetSashPosition(-1)
1788        # self.canvas.OnSize()
1789        pass
1790
1791
1792class TestMainframe(AgileToolbarFrameMixin, wx.Frame):
1793    """
1794    Simple wx frame with some special features.
1795    """
1796
1797    def __init__(self, parent=None,  id=-1, title='mainframe', pos=wx.DefaultPosition,
1798                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
1799                 name='frame'):
1800
1801        # Forcing a specific style on the window.
1802        #   Should this include styles passed?
1803        style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1804        wx.Frame.__init__(self, parent, id, title, pos, size=size, style=style, name=name)
1805        #super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)
1806        self._splitter = MainSplitter(self)
1807        self._views = {}
1808        #wx.EVT_SIZE  (self, self.on_size)
1809        # sizer=wx.BoxSizer(wx.VERTICAL)
1810        # sizer.Add(p1,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)# from NaviPanelTest
1811        # sizer.Add(self.canvas,1,wx.GROW)# from NaviPanelTest
1812
1813        # finish panel setup
1814        # self.SetSizer(sizer)
1815        # sizer.Fit(self)
1816        # self.Show()
1817
1818        # this is needed to initialize GL projections for unproject
1819        # wx.CallAfter(self.on_size)
1820
1821        #width,height = self.GetSize()
1822        #self._splitter.SetSashPosition(300, True)
1823        # maximize the frame
1824        self.Maximize()
1825        # self.CenterOnScreen()
1826        #################################################################
1827        # create statusbar
1828        #self.statusbar = AgileStatusbar(self)
1829        self.statusbar = AgileStatusbar(self)
1830        self.SetStatusBar(self.statusbar)
1831        # self.count=0.0
1832
1833        #################################################################
1834        # create toolbar
1835
1836        tsize = (16, 16)
1837        self.init_toolbar(size=tsize)
1838
1839        #new_bmp =  wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR, tsize)
1840        #open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize)
1841        #save_bmp= wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize)
1842        #cut_bmp = wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR, tsize)
1843        #copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR, tsize)
1844        #paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR, tsize)
1845
1846        #self.add_tool('new',self.on_open,new_bmp,'create new doc')
1847        #self.add_tool('open',self.on_open,open_bmp,'Open doc')
1848        #self.add_tool('save',self.on_save,save_bmp,'Save doc')
1849        # self.toolbar.AddSeparator()
1850        # self.add_tool('cut',self.on_open,cut_bmp,'Cut')
1851        # self.add_tool('copy',self.on_open,copy_bmp,'Copy')
1852        # self.add_tool('paste',self.on_open,paste_bmp,'Paste')
1853
1854        # self.SetToolBar(self.toolbar)
1855
1856        #################################################################
1857        # create the menu bar
1858
1859        self.menubar = AgileMenubar(self)
1860        self.menubar.append_menu('file')
1861        self.menubar.append_menu('file/doc')
1862
1863        self.menubar.append_item('file/doc/open', self.on_open,
1864                                 shortkey='Ctrl+o', info='open it out')
1865
1866        self.menubar.append_item('file/doc/save', self.on_save,
1867                                 shortkey='Ctrl+s', info='save it out')
1868
1869        # self.menubar.append_menu('edit')
1870        # self.menubar.append_item('edit/cut',self.cut,\
1871        #                    shortkey='Ctrl+c',info='cut it out')
1872
1873        # self.menubar.append_item('edit/toggle',self.toggle_tools,\
1874        #                    shortkey='Ctrl+t',info='toggle tools')
1875        # self.menubar.append_menu('tools')
1876        self.SetMenuBar(self.menubar)
1877        # self.Show(True) #NO!!
1878
1879    def add_view(self, name, ViewClass, **args):
1880        """
1881        Add a new view to the notebook.
1882        """
1883        # print 'context.add_view',ViewClass
1884        # print '  args',args
1885
1886        view = self._splitter.add_view(name, ViewClass, **args)
1887        self._views[name] = view
1888        # self._viewtabs.SetSelection(p)
1889        # self._splitter._viewtabs.Show(True)
1890        return view
1891
1892    def on_size(self, event=None):
1893        print 'Mainframe.on_size'
1894        # self.tc.SetSize(self.GetSize())
1895        # self.tc.SetSize(self.GetSize())
1896        # self._viewtabs.SetSize(self.GetSize())
1897        # pass
1898        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1899        #wx.LayoutAlgorithm().LayoutWindow(self, self.p1)
1900
1901        # important:
1902        #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs)
1903        wx.LayoutAlgorithm().LayoutWindow(self, self._splitter)
1904        if event:
1905            event.Skip()
1906
1907    def on_save(self, event):
1908        print 'save it!!'
1909
1910    def on_open(self, event):
1911        """Open a document"""
1912        #wildcards = CreateWildCards() + "All files (*.*)|*.*"
1913        print 'open it!!'
1914
1915    def destroy(self):
1916        """Destroy this object"""
1917        # self.theDocManager.theDestructor()
1918        #imgPreferences.saveXml(self.GetStartDirectory() + "/" + imgINI_FILE_NAME)
1919        ##del self.thePrint
1920        self.Destroy()
1921
1922    def on_close(self, event):
1923        # self.Close(True)
1924        # pass
1925        self.destroy()
1926
1927    def on_exit(self, event):
1928        """Called when the application is to be finished"""
1929        self.destroy()
1930
1931    def on_idle(self, event):
1932        pass
1933        #self.count = self.count + 1
1934        # if self.count >= 100:
1935        #    self.count = 0
1936
1937        # self.statusbar.set_progress(self.count)
1938
1939    def on_about(self, event):
1940        """Display the information about this application"""
1941        #dlg = imgDlgAbout(self, -1, "")
1942        # dlg.ShowModal()
1943        # dlg.Destroy()
1944        pass
1945
1946
1947linewidth = 3
1948vertices = [
1949    [[0.0, 0.0, 0.0], [0.2, 0.0, 0.0]],  # 0 green
1950    [[0.0, 0.0, 0.0], [0.0, 0.9, 0.0]],  # 1 red
1951]
1952
1953
1954colors = [
1955    [0.0, 0.9, 0.0, 0.9],    # 0
1956    [0.9, 0.0, 0.0, 0.9],    # 1
1957]
1958lines = Lines(linewidth=linewidth, vertices=vertices, colors=colors)
1959
1960linewidth2 = 3
1961vertices2 = [
1962    [[0.5, 0.5, 0.0], [0.7, 0.5, 0.0], [0.7, 1.0, 0.0]],  # 0 green
1963    [[0.8, 0.5, 0.0], [0.9, 0.8, 0.0], [0.8, 0.2, 0.0]],  # 1 orange
1964]
1965colors2 = [
1966    [0.0, 0.9, 0.3, 0.9],    # 0
1967    [0.9, 0.3, 0.0, 0.9],    # 1
1968]
1969triangles = Triangles(linewidth=linewidth2, vertices=vertices2, colors=colors2)
1970
1971linewidth3 = 3
1972vertices3 = [
1973    [[0.5, 0.0, 0.0], [0.7, 0.0, 0.0], [0.7, 0.3, 0.0], [0.5, 0.3, 0.0], ],  # 0
1974    [[0.1, 0.0, 0.0], [0.3, 0.0, 0.0], [0.3, 0.2, 0.0], [0.1, 0.2, 0.0], ],  # 1
1975]
1976colors3 = [
1977    [0.8, 0.0, 0.8, 0.9],    # 0
1978    [0.0, 0.6, 0.6, 0.9],    # 1
1979]
1980rectangles = Rectangles(linewidth=linewidth3, vertices=vertices3, colors=colors3)
1981
1982if __name__ == '__main__':
1983
1984    app = wx.PySimpleApp()
1985
1986    if 1:
1987
1988        frame = TestMainframe()
1989        gleditor = frame.add_view('GL Editor', GlEditor)
1990        #gleditor = frame._splitter.add_view('GL Editor',GlEditor)
1991        frame.Show()
1992        frame.on_size()
1993        canvas = gleditor.get_canvas()
1994        canvas.add_element(lines)
1995        canvas.add_element(triangles)
1996        canvas.add_element(rectangles)
1997
1998    app.SetTopWindow(frame)
1999    app.MainLoop()
2000
2001    app.Destroy()
2002