Ejemplo n.º 1
0
    def test_001(self):
        print("test_001:")
        sg = Singulersum(1.0)
        test = """
yaml:
    application: Singulersum
    url: https://github.com/frazy81/Singulersum
    min-version: 2021-04-02
    min-yaml-version: 2021-04-02
sg:
    scale: 5.0
animator:
    type: animation
    stop: 1.0
    start: 0.0
    camera: cam
    begin: [1.2, 2.0, 1.0]
    end: [1.6, 0.1, 0.5]
    x: begin[0] + (time*(end[0] - begin[0]))
    y: begin[1] + (time*(end[1] - begin[1]))
    z: begin[2] + (time*(end[2] - begin[2]))
        """
        sg.yaml(data=test)

        self.assertEqual(sg.scale, 5.0)

        print("test_001 end.")
        print()

        pass
Ejemplo n.º 2
0
    def test_001(self):
        sg = Singulersum()
        cam = sg.camera(1, 0, 0, 0, 0, 0, name="cam1")
        P = (1, 0, 0)
        print("P:", cam.vec_show(P))
        P_prime = cam.rotate(P, 90, 0)
        print("P_prime:", cam.vec_show(P_prime))
        self.assertTrue(P_prime[0]-0.0<1e-6)  # x=0, y=1, z=0
        self.assertTrue(P_prime[1]-1.0<1e-6)
        self.assertTrue(P_prime[2]-0.0<1e-6)
        P_prime = cam.rotate(P, 90, 90)
        print("P_prime:", cam.vec_show(P_prime))
        # what here? (0,1,0) 90° on Y-axis???
        #self.assertTrue(P_prime[0]-0.0<1e-6)  # x=0, y=1, z=1
        #self.assertTrue(P_prime[1]-1.0<1e-6)
        #self.assertTrue(P_prime[2]-0.0<1e-6)

        P_prime = cam.rotate(P, 10, 0)
        print("P_prime 10° rotated: ", cam.vec_show(P_prime))
        P_prime = cam.rotate(P_prime, -10, 0)
        print("P_prime 10° rotated back again: ", cam.vec_show(P_prime))
        self.assertTrue(abs(P_prime[0]-P[0])<1e-6)
        self.assertTrue(abs(P_prime[1]-P[1])<1e-6)
        self.assertTrue(abs(P_prime[2]-P[2])<1e-6)

        pass
Ejemplo n.º 3
0
    def test_006(self):
        print("test_006:")
        sg = Singulersum(1.0, callback=self.callback)
        file = "../yaml/singulersum.yaml"

        data = sg.yaml(file=file)

        print("test_006 end.")
        print()
Ejemplo n.º 4
0
    def test_003(self):
        print("test_003:")
        sg = Singulersum(1.0, callback=self.callback)
        file = "../yaml/sine_waves.yaml"

        self.animated = False

        data = sg.yaml(file=file)

        self.assertEqual(self.animated, True)

        print("test_003 end.")
        print()
Ejemplo n.º 5
0
    def __init__(self, verbose=True, exitImmediate=False, quitAfterAnimation=False, quitAfter=None, inputFile=None):
        super().__init__()

        self.startTime = time.time()

        # configureCanvas is changing these to the actual canvas size
        self.width = 2048
        self.height = 2048
        self.cam = None

        self.verbose = verbose
        self.quitAfterAnimation = quitAfterAnimation
        self.quitAfter = quitAfter

        self.isShowing = True      # self.show() running?
        self.isTerminating = False
        if exitImmediate is True:
            self.isTerminating = True
        self.animated = False       # this is set by a yaml. calling callback()
        self.framesShown = 0
        self.isPlaying = True
        self.objectBrowser = False
        self.settingsBrowser = False
        self.selectedItem = None    # sg def first

        self.sg = Singulersum(scale=(5.0, 5.0, 5.0), callback=self.callback)
        self.sg.logfile("./singulersum_gui.log")

        # setup default camera
        self.cam = self.sg.camera(1.0, 0.3, 0.4, 0.0, 0.0, 0.0, name="default")
        self.camera = "default"
        self.canvasInteract = CanvasInteract(self, self.cam)
        self.newCamera(self.camera)

        if inputFile is not None:
            # load the file
            self.open(inputFile)
        else:
            # load default
            self.sg.yaml("../yaml/lighttest.yaml")
            # each call to yaml will callback to self.callback and sets the camera that
            # is defined within the yaml file.
            pass

        self.selectedItem = self.sg

        self.sg.setTime(0)

        self.createWidgets()

        self.show()
Ejemplo n.º 6
0
    def test_005(self):
        print("test_005:")
        sg = Singulersum(1.0, callback=self.callback)
        file = "../yaml/singulersum.yaml"

        data = sg.yaml(file=file)

        self.assertEqual(sg.scale, 5.0)

        print("tangential_plane_e.x:", sg.objects["tangential_plane_e"].x)
        self.assertEqual(sg.objects["tangential_plane_e"].x, -1.0)

        print("test_005 end.")
        print()
Ejemplo n.º 7
0
    def test_004(self):
        print("test_004:")
        sg = Singulersum(1.0, callback=self.callback)
        file = "../yaml/tiny_house.yaml"

        self.animated = False

        data = sg.yaml(file=file)

        print(data)

        self.assertEqual(self.animated, True)
        self.assertEqual(self.camera, "cam1")
        print("test_004 end.")
        print()
Ejemplo n.º 8
0
    def test_002(self):
        sg = Singulersum()
        cam = sg.camera(1, 0, 0, 0, 0, 0, name="cam1")

        self.V = [-1, 0, 0]
        print("V initial:", cam.vec_show(self.V))
        self.V = cam.rotate(self.V, 40)
        self.V = cam.rotate(self.V, 0, 60)
        print("V rotated:", cam.vec_show(self.V))
        self.lV = cam.vec_len(self.V)
        self.T = self.V
        self.T = cam.rotate(self.T, 0, -60)
        self.T = cam.rotate(self.T, -40, 0)
        print("V test reversed:", cam.vec_show(self.T))
        # ah, not commutative!

        print("testing V:")

        azimuth = atan2(self.V[1], self.V[0])
        #if azimuth<0.0:
        #    azimuth = 2*pi+azimuth
        
        self.view_azimuth = azimuth/pi*180
        self.view_radius  = self.lV
        self.view_altitude = atan2(self.V[2], sqrt(self.V[0]**2+self.V[1]**2))/pi*180
        print("View vector azimuth             ", "{:4f}".format(self.view_azimuth))
        print("View vector altitude:           ", "{:4f}".format(self.view_altitude))
        print("View vector 'radius'=length(V): ", "{:4f}".format(self.view_radius))

        print("after rotation:")

        # now we back transform View Vector and Camera position
        print("rotate back")
        print(180-self.view_azimuth)
        print(-1*self.view_altitude)
        self.V_prime = cam.rotate(self.V, 180-self.view_azimuth, -1*self.view_altitude, 0.0)
        print("Camera View Vector V_prime:     ", cam.vec_show(self.V_prime))
        print("Camera V_prime length:          ", "{:4f}".format(cam.vec_len(self.V_prime)))
        assert(abs(self.V_prime[0]-(-1.0))<0.5)
        assert(abs(self.V_prime[1]-0.0)<0.5)
        assert(abs(self.V_prime[2]-0.0)<0.5)
        self.V_prime = (-1.0, 0, 0)
Ejemplo n.º 9
0
    def test_007(self):
        print("test_007:")
        sg = Singulersum(1.0, callback=self.callback)

        self.versionOk = True
        self.testing = False

        test = """
yaml:
    application: Singulersum
    url: https://github.com/frazy81/Singulersum
    min-version: 9999-12-31
    min-yaml-version: 9999-12-31
gui:
    testing: True
sg:
    scale: 5.0
animator:
    type: animation
    stop: 1.0
    start: 0.0
    camera: cam
    begin: [1.2, 2.0, 1.0]
    end: [1.6, 0.1, 0.5]
    x: begin[0] + (time*(end[0] - begin[0]))
    y: begin[1] + (time*(end[1] - begin[1]))
    z: begin[2] + (time*(end[2] - begin[2]))
        """
        sg.yaml(data=test)

        self.assertFalse(self.versionOk)
        self.assertTrue(self.testing)

        self.assertEqual(sg.scale, 5.0)

        print("test_007 end.")
        print()

        pass
Ejemplo n.º 10
0
    def __init__(self):
        super().__init__()

        self.isPaused = False

        sg = Singulersum(scale=(5, 5, 5), callback=self.callback)
        self.sg = sg

        #sg.yaml("../yaml/sine_waves.yaml")
        #sg.yaml("../yaml/tiny_house.yaml")
        #sg.yaml("../yaml/sphericon_ascii_stl.yaml")
        #sg.yaml("../yaml/utah_teapot_stl.yaml")
        #sg.yaml("../yaml/millennium_falcon_stl.yaml")
        #sg.yaml("../yaml/heart_curve.yaml")
        #sg.yaml("../yaml/sink.yaml")
        #sg.yaml("../yaml/cube.yaml")
        sg.yaml("../yaml/lighttest.yaml")

        self.cam = self.sg.cameras[self.camera]

        self.createWidgets()

        self.mainloop_singulversum()
Ejemplo n.º 11
0
    def test_002(self):
        print("test_002:")
        sg = Singulersum(1.0, callback=self.callback)
        test = """
gui:
    isPlaying: True
sg:
    scale: 5.0
    stop: 1.0
animator:
    type: animation
    stop: 1.0
    start: 0.0
    begin: [0, 0, 0]
    end: [1, 1, 1]
    x: begin[0] + (time*(end[0] - begin[0]))
    y: begin[1] + (time*(end[1] - begin[1]))
    z: begin[2] + (time*(end[2] - begin[2]))
cam:
    type: camera
    position: [1.0, 0.1, 0.2]
    lookat: [0.0, 0.0, 0.0]
    update: animator
f1:
    type: function
    visibility: True
    fx: x
    fy: y
    fz: sin(x)+sin(y)
    rel: z
    scale: 5.0
    size: 2.0
f2:
    type: function
    visibility: False
    fx: x
    fy: y
    fz: sin(x)+sin(y)
    rel: z
    scale: 5.0
    size: 2.0
p1:
    type: point
    point: [5.0, 5.0, 5.0]

        """

        data = sg.yaml(data=test)

        print(data)

        self.assertEqual(sg.scale, 5)

        self.assertEqual(sg.cameras["cam"].x, 1)
        self.assertEqual(sg.cameras["cam"].y, 0.1)
        self.assertEqual(sg.cameras["cam"].z, 0.2)

        self.assertEqual(sg.objects["f1"].visibility, True)

        self.assertEqual(sg.objects["f2"].visibility, False)

        sg.setTime(0.5)
        self.assertEqual(sg.time, 0.5)

        print("update 1 for time=0.5")
        sg.update()
        print(sg.cameras["cam"].x, "=?", 0.5)
        self.assertTrue(abs(sg.cameras["cam"].x - 0.5) < 0.1)
        print("done update 1")
        sg.setTime(0.9)
        print("update 2 for time=0.9")
        sg.update()
        print(sg.cameras["cam"].x, "=?", 0.9)
        self.assertTrue(abs(sg.cameras["cam"].x - 0.9) < 0.1)
        print("done update 2")

        print("test_002 end.")
        print()
Ejemplo n.º 12
0
    def test_001(self):
        sg = Singulersum()
        cam = sg.camera(1, 0, 0, 0, 0, 0, name="cam1")
        cube = sg.cube(r=1.0, name="test")
        self.assertTrue("top1" in cube.objects)
        top = cube.objects["top1"]
        self.assertTrue(top)
        print(top.points)
        p0 = top.points[0]
        p1 = top.points[1]
        p2 = top.points[2]
        self.assertEqual(p0[0], 1.0)
        self.assertEqual(p0[1], -1.0)
        self.assertEqual(p0[2], 1.0)

        self.assertEqual(p1[0], 1.0)
        self.assertEqual(p1[1], 1.0)
        self.assertEqual(p1[2], 1.0)

        self.assertEqual(p2[0], -1.0)
        self.assertEqual(p2[1], 1.0)
        self.assertEqual(p2[2], 1.0)

        print("p0", p0)
        print("p1", p1)
        print("p2", p2)
        print("vector p0p1", cam.vec_sub(p1,p0))
        print("vector p0p2", cam.vec_sub(p2,p0))
        normalvector = cam.poly_normalvector(p0, p1, p2)
        print("normalvector", normalvector)

        self.assertEqual(normalvector[0], 0.0)
        self.assertEqual(normalvector[1], 0.0)
        self.assertEqual(normalvector[2], 1.0)

        # same for the second poly of "top"
        print("top2")
        top = cube.objects["top2"]
        self.assertTrue(top)
        print(top.points)
        p0 = top.points[0]
        p1 = top.points[1]
        p2 = top.points[2]
        self.assertEqual(p0[0], 1.0)
        self.assertEqual(p0[1], -1.0)
        self.assertEqual(p0[2], 1.0)

        self.assertEqual(p1[0], -1.0)
        self.assertEqual(p1[1], 1.0)
        self.assertEqual(p1[2], 1.0)

        self.assertEqual(p2[0], -1.0)
        self.assertEqual(p2[1], -1.0)
        self.assertEqual(p2[2], 1.0)

        print("p0", p0)
        print("p1", p1)
        print("p2", p2)
        print("vector p0p1", cam.vec_sub(p1,p0))
        print("vector p0p2", cam.vec_sub(p2,p0))
        normalvector = cam.poly_normalvector(p0, p1, p2)
        print("normalvector", normalvector)

        self.assertEqual(normalvector[0], 0.0)
        self.assertEqual(normalvector[1], 0.0)
        self.assertEqual(normalvector[2], 1.0)

        pass
Ejemplo n.º 13
0
class SingulersumGUI(Tk):

    def __init__(self, verbose=True, exitImmediate=False, quitAfterAnimation=False, quitAfter=None, inputFile=None):
        super().__init__()

        self.startTime = time.time()

        # configureCanvas is changing these to the actual canvas size
        self.width = 2048
        self.height = 2048
        self.cam = None

        self.verbose = verbose
        self.quitAfterAnimation = quitAfterAnimation
        self.quitAfter = quitAfter

        self.isShowing = True      # self.show() running?
        self.isTerminating = False
        if exitImmediate is True:
            self.isTerminating = True
        self.animated = False       # this is set by a yaml. calling callback()
        self.framesShown = 0
        self.isPlaying = True
        self.objectBrowser = False
        self.settingsBrowser = False
        self.selectedItem = None    # sg def first

        self.sg = Singulersum(scale=(5.0, 5.0, 5.0), callback=self.callback)
        self.sg.logfile("./singulersum_gui.log")

        # setup default camera
        self.cam = self.sg.camera(1.0, 0.3, 0.4, 0.0, 0.0, 0.0, name="default")
        self.camera = "default"
        self.canvasInteract = CanvasInteract(self, self.cam)
        self.newCamera(self.camera)

        if inputFile is not None:
            # load the file
            self.open(inputFile)
        else:
            # load default
            self.sg.yaml("../yaml/lighttest.yaml")
            # each call to yaml will callback to self.callback and sets the camera that
            # is defined within the yaml file.
            pass

        self.selectedItem = self.sg

        self.sg.setTime(0)

        self.createWidgets()

        self.show()

    def callback(self, event, **args):
        print("TkGUI, got callback from Singulersum:", event, args)
        if event=="set":
            setattr(self, args["name"], args["value"])
            if args["name"]=="animated":
                if args["value"]==True:
                    print("TkGUI.callback() got an animated=True, therefore start playing")
                    self.isPlaying=True
                    self.isShowing=True
                else:
                    self.isShowing=True
                    self.isPlaying=False
            if args["name"]=="camera":
                # set the camera, so that the GUI knows which camera to get images from.
                self.newCamera(args["value"])
        elif event=="animation_start":
            # TODO: this is nowhere fired!
            self.play()
        elif event=="animation_stop":
            self.pause()
            print("sleep (end of animation). Press play to play again.")
            self.isPaused=True
            self.pauseVar.set('play')
            self.sg.setTime(0.0)
            self.update()
            self.update_idletasks()
            if self.quitAfterAnimation is True:
                print("quitAfterAnimation is True: quit application now.")
                self.quit()
        pass

    def newCamera(self, value):
        current_width = self.width
        current_height = self.height
        if self.cam is not None:
            current_width = self.cam.width
            current_height = self.cam.height
        # singulversum updated it's default camera, so we need to setup the new
        # camera and make a new CanvasInteract for mouse operations.
        self.cam = self.sg.cameras[value]
        # initialize camera with the current canvas width/height
        self.cam.width = current_width
        self.cam.height = current_height
        self.canvasInteract.camera(self.cam)

    def showImage(self):
        self.framesShown += 1
        imgdata = self.cam.image()
        self.sg.debug("Tk picture import")
        timeit = self.sg.timeit()
        image = Image.frombuffer("RGBA", (self.cam.draw2d.width,self.cam.draw2d.height), imgdata, decoder_name="raw").convert("RGB")
        self.img = PhotoImage(image)
        self.sg.debug("Tk picture import complete.", timeit=timeit)

        timeit = self.sg.timeit()
        self.canvas.create_image(0, 0, anchor=NW, image=self.img)
        self.updateWidgets()
        self.sg.debug("update Tk Tasks")
        self.sg.debug("Tk Tasks updated, picture outlined", timeit=timeit)

    # show(), the mainloop for SingulversumGUI!
    def show(self):
        while True:
            print("state: isShowing=", self.isShowing, "isPlaying=", self.isPlaying)
            if self.quitAfter is not None and self.startTime+self.quitAfter<=time.time():
                print("quitAfter "+str(self.quitAfter)+"s elapsed. Terminating in 2s.")
                time.sleep(2)
                self.isTerminating=True
            if self.isTerminating is True:
                # terminating is stepping out of this while!
                print("show(): received terminate instruction. Finish work and close application") # needs to be done here, since everything else is async and only this method is actually always running. Therefore a sleep() in quit() does not change anything... Kind of. Well it's strange...
                print(self.framesShown, "frames shown.")
                self.destroy()
                return True
            if self.isShowing is False:
                print("idle")
                self.update()
                self.update_idletasks()
                print("sleep 0.5")
                time.sleep(0.5)
                continue
            else:
                print()
            if self.isPlaying is False:
                self.isShowing=False        # set early! if mouse event occurs in between
                print("stop playing")
                self.sg.update()    # stl air intake problem, no update before show
                self.showImage()
                self.update()
                self.update_idletasks()
                continue

            self.sg.timeAdvance()
            self.sg.update()
            self.showImage()

    def play(self):
        print("TkGUI: play clicked")
        # TODO: reinitialize camera update function which might have been destroyed by
        # CanvasInteract() class
        if self.isPlaying is False:
            self.isShowing=True
            self.isPlaying=True
            self.pauseVar.set('pause')

    def pause(self):
        self.isPlaying=False
        self.pauseVar.set('play')

    def rewind(self):
        print("TkGUI: rewind clicked")
        # TODO: reinitialize camera update function which might have been destroyed by
        # CanvasInteract() class
        self.isPlaying=False
        self.sg.setTime(0.0)
        self.sg.update()
        print("start to play again")
        self.isPlaying=True
        self.isShowing=True
        self.pauseVar.set('pause')

    def repaint(self):
        print("TkGUI: repaint clicked")
        self.isShowing=True

    def createWidgets(self):
        self.resizable( height=True, width=True )
        self.geometry( "{:d}x{:d}".format(self.width, self.height) )
        self.title("Singulersum V{:s}".format(Singulersum.version))

        # to minimize on close
        #self.protocol("WM_DELETE_WINDOW", self.iconify)
        self.protocol("WM_DELETE_WINDOW", self.quit)

        # menu
        menubar = Menu(self)
        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label="New", command=lambda: self.newFile())
        filemenu.add_command(label="Open", command=lambda: self.openFile())
        filemenu.add_command(label="Save (not implemented)", command=lambda: self.notImpl())
        filemenu.add_command(label="Save as... (not implemented)", command=lambda: self.notImpl())
        filemenu.add_command(label="Close", command=lambda: self.newFile())

        filemenu.add_separator()

        filemenu.add_command(label="Exit", command=self.quit)
        menubar.add_cascade(label="File", menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)

        editmenu.add_command(label="add point", command=lambda: self.addObject("point", x=0.0, y=0.0, z=0.0, color="#ff0000"))
        editmenu.add_command(label="add line", command=lambda: self.addObject("line", x1=0.0, y1=0.0, z1=0.0, x2=0.0, y2=0.0, z2=0.0, color="#ff0000"))
        # TODO: sphere gets distorted, x=0, y=1, z=0.5, r=0.3
        editmenu.add_command(label="add sphere", command=lambda: self.addObject("sphere", x=0.0, y=0.0, z=0.0, r=0.3, size=1.0, color="#ff0000"))
        editmenu.add_command(label="add cube", command=lambda: self.addObject("cube", x=0.0, y=0.0, z=0.0, r=0.3, size=1.0, color="#aaaaaa"))

        editmenu.add_separator()
        editmenu.add_command(label="Cut (not implemented)", command=lambda: self.notImpl())
        editmenu.add_command(label="Copy (not implemented)", command=lambda: self.notImpl())
        #editmenu.add_command(label="Undo (not implemented)", command=lambda: self.notImpl())
        #editmenu.add_command(label="Paste", command=lambda: self.notImpl())
        #editmenu.add_command(label="Delete", command=lambda: self.notImpl())
        #editmenu.add_command(label="Select All", command=lambda: self.notImpl())
        menubar.add_cascade(label="Edit", menu=editmenu)

        examplesmenu = Menu(menubar, tearoff=0)
        examplesmenu.add_command(label="singulersum", command=lambda: self.openYaml("../yaml/singulersum.yaml"))
        examplesmenu.add_command(label="test", command=lambda: self.openYaml("../yaml/test.yaml"))
        examplesmenu.add_command(label="tiny house", command=lambda: self.openYaml("../yaml/tiny_house.yaml"))
        examplesmenu.add_command(label="lighttest", command=lambda: self.openYaml("../yaml/lighttest.yaml"))
        examplesmenu.add_command(label="cube", command=lambda: self.openYaml("../yaml/cube.yaml"))
        examplesmenu.add_command(label="sine waves", command=lambda: self.openYaml("../yaml/sine_waves.yaml"))
        examplesmenu.add_command(label="sphericon", command=lambda: self.openYaml("../yaml/sphericon_ascii_stl.yaml"))
        examplesmenu.add_command(label="utah teapot", command=lambda: self.openYaml("../yaml/utah_teapot_stl.yaml"))
        examplesmenu.add_command(label="millennium falcon", command=lambda: self.openYaml("../yaml/millennium_falcon_stl.yaml"))
        examplesmenu.add_command(label="heart curve", command=lambda: self.openYaml("../yaml/heart_curve.yaml"))
        examplesmenu.add_command(label="sink", command=lambda: self.openYaml("../yaml/sink.yaml"))

        menubar.add_cascade(label="Examples", menu=examplesmenu)

        settings = Menu(menubar, tearoff=0)
        settings.add_command(label="Singulersum", command=lambda: self.configSingulersum(Toplevel(self)))

        settings.add_separator()

        menubar.add_cascade(label="Settings", menu=settings)

        helpmenu = Menu(menubar, tearoff=0)
        helpmenu.add_command(label="Help Index (not implemented)", command=lambda: self.notImpl())
        helpmenu.add_command(label="About...", command=lambda: self.help())
        menubar.add_cascade(label="Help", menu=helpmenu)

        self.config(menu=menubar)

        # main panel
        self.functionbar = Frame(self, highlightbackground="black", highlightthickness=1)
        self.functionbar.pack(side=TOP, anchor=W)

        self.center = Frame(self)
        self.center.pack(side=TOP, fill=BOTH, expand=True)
        self.objectBrowserPanel = Frame(self)
        self.content = Frame(self.center, highlightbackground="black", highlightthickness=1)
        self.content.pack(side=LEFT, fill=BOTH, expand=True)
        self.settingsBrowserPanel = Frame(self)

        # function bar (top)
        bar = self.functionbar
        Button(bar, text="objects", command=lambda: self.guiUpdate("objectBrowser") ).pack(side=LEFT, anchor=W)
        Button(bar, text="settings", command=lambda: self.guiUpdate("settingsBrowser") ).pack(side=LEFT, anchor=W)

        # controlls
        self.controlls = Frame(self, highlightbackground="black", highlightthickness=1)
        self.controlls.pack(side=TOP)

        self.pauseVar = StringVar()
        self.pauseVar.set("pause")
        self.pauseBtn = Button(self.controlls, textvariable=self.pauseVar, command=lambda: self.pause() if self.isPlaying else self.play())
        self.pauseBtn.pack(side=LEFT)
        self.rewindBtn = Button(self.controlls, text="rewind", command=self.rewind)
        self.rewindBtn.pack(side=LEFT)
        self.repaintBtn = Button(self.controlls, text="repaint", command=self.repaint)
        self.repaintBtn.pack(side=LEFT)

        # status
        self.status = Frame(self)
        self.status.pack(side=TOP)
        self.statusLine = Label(self.status, text="Status: Ready")
        self.statusLine.pack(side=LEFT)
        self.fpsNum = StringVar()
        self.fps = Label(self.status, textvariable=self.fpsNum)
        self.fps.pack(side=LEFT)
        self.camPos = StringVar()
        self.lcamPos = Label(self.status, textvariable=self.camPos)
        self.lcamPos.pack(side=LEFT)
        self.viewVec = StringVar()
        self.lviewVec = Label(self.status, textvariable=self.viewVec)
        self.lviewVec.pack(side=LEFT)
        self.sphericalVec = StringVar()
        self.lsphericalVec = Label(self.status, textvariable=self.sphericalVec)
        self.lsphericalVec.pack(side=LEFT)
        self.createViewer()

    def guiUpdate(self, name=None):
        if name is not None:
            val = getattr(self, name)
            if val is False:
                val = True
            else:
                val = False
            setattr(self, name, val)

        if self.objectBrowser is True:
            self.objectBrowserPanel.place(anchor=NW, x=0, y=100, relwidth=0.4, relheight=0.9)
        else:
            for child in self.settingsBrowserPanel.winfo_children():
                child.destroy()
            self.objectBrowserPanel.place_forget()
        if self.settingsBrowser is True:
            self.settingsBrowserPanel.place(anchor=NE, relx=1, y=100, relwidth=0.4, relheight=0.9)
            f=Frame(self.settingsBrowserPanel)
            f.pack(side=TOP, anchor=NW)
            if self.selectedItem==self.sg:
                self.configSingulersum(f)
        else:
            self.settingsBrowserPanel.place_forget()
            for child in self.settingsBrowserPanel.winfo_children():
                child.destroy()

    def addObject(self, object_type, **args):
        row = 0
        column = 0

        top = Toplevel(self)

        # coming from menu, settings placed in a Toplevel (new Window)
        top.title("add a new singulersum object")
        top.geometry( "{:d}x{:d}".format(800, int(800/4*2)) )
        top.bind('<Escape>', lambda e: top.destroy())

        entries = []

        label = Label(top, text="")
        label.grid(row=row, column=column, sticky=W)
        row+=1

        for name, value in args.items():
            label = Label(top, text=name)
            label.grid(row=row, column=column, sticky=W)
            column+=1
            objectsetting = Entry(top)
            objectsetting.insert(0, value)
            entries.append( { "object_type":object_type, "name":name, "value":objectsetting, "type":type(value).__name__ } )
            objectsetting.grid(row=row, column=column, sticky=W)
            column=0
            row+=1

        button = Button(top, text="Apply", command=lambda: self.addObjectMake(top, entries, True))
        button.grid(row=row, column=column, sticky=W)
        column+=1
        button = Button(top, text="Cancel", command=lambda: self.addObjectMake(top, entries, False))
        button.grid(row=row, column=column, sticky=W)

    def addObjectMake(self, top, entries, apply=False):
        if apply is True:
            args = {}
            for entry in entries:
                value = entry["value"].get()
                if entry["type"]=="float":
                    value = float(value)
                elif entry["type"]=="bool":
                    value = bool(value)
                elif entry["type"]=="string":
                    value = str(value)
                name = entry["name"]
                args[name]=value
            type = entries[0]["object_type"]
            obj=getattr(self.sg, type)(**args)
            print("TkGUI.addObjectMake(): made a new object", obj)
        top.destroy()
        self.isShowing=True

    def quit(self):
        print("terminating the application")
        self.isTerminating=True
        self.sg.quit()
        print("wait for show() to finish.")
        self.update()
        self.update_idletasks()

    def updateWidgets(self):
        self.fpsNum.set("fps="+"{:0.2f}".format(self.sg.fps))
        self.camPos.set("cam=[{:0.2f}, {:0.2f}, {:0.2f}]".format(self.cam.x, self.cam.y, self.cam.z))
        self.viewVec.set("view at [{:0.2f}, {:0.2f}, {:0.2f}]".format(self.cam.V[0], self.cam.V[1], self.cam.V[2]))
        self.sphericalVec.set("sherical [{:0.2f}, {:0.2f}, {:0.2f}]".format(self.cam.cam_azimuth, self.cam.cam_altitude, self.cam.cam_radius))
        self.update()
        self.update_idletasks()

    def openYaml(self, file):
        print("TkGUI(): open yaml file", file)
        self.sg.reset()
        self.sg.yaml(file)
        self.isPlaying=True
        self.isShowing=True

    def openStl(self, file):
        print("TkGUI(): open stl file", file)
        self.sg.reset()
        self.sg.stl(file)
        self.isPlaying=False
        self.isShowing=True

    def newFile(self):
        print("TkGUI(): newFile")
        self.pause()
        self.sg.reset()
        self.sg.setTime(0)
        self.isShowing=True
        self.isPlaying=False

    def open(self, file):
        if file[-4:]==".stl":
            self.openStl(file)
        elif file[-5:]==".yaml":
            self.openYaml(file)
        else:
            self.debug("don't know how to handle this file type (must be .yaml or .stl)", file)

    def openFile(self):
        dlg = filedialog.Open(self, filetypes=(("all files", "*.*"), ("YAML files", "*.yaml"), ("STL files", "*.stl")))
        file = dlg.show()
        self.open(file)

    def notImpl(self):
        top = Toplevel(self)
        top.bind('<Escape>', lambda e: top.destroy())
        top.title("Not Implemented")
        top.geometry( "{:d}x{:d}".format(600, int(600/4*2)) )
        label = Label(top, text="")
        label.pack(side=TOP)
        label = Label(top, text="Not yet implemented.")
        label.pack(side=TOP)
        label = Label(top, text="")
        label.pack(side=TOP)
        button = Button(top, text="Cancel", command=lambda: top.destroy())
        button.pack(side=TOP)

    def help(self):
        top = Toplevel(self)
        top.bind('<Escape>', lambda e: top.destroy())
        top.title("Singulersum help")
        top.geometry( "{:d}x{:d}".format(600, int(600/4*2)) )
        label = Label(top, text="")
        label.pack(side=TOP)
        label = Label(top, text="2021-03-05 Philipp Hasenfratz")
        label.pack(side=TOP)
        label = Label(top, text="Singulersum is a prototype for a")
        label.pack(side=TOP)
        label = Label(top, text="3D rendering engine")
        label.pack(side=TOP)
        label = Label(top, text="")
        label.pack(side=TOP)
        button = Button(top, text="Cancel", command=lambda: top.destroy())
        button.pack(side=TOP)

    def configSingulersum(self, top):
        row = 0
        column = 0

        if top!=self and isinstance(top, Toplevel) is True:
            # coming from menu, settings placed in a Toplevel (new Window)
            top.title("configure Singulersum object")
            top.geometry( "{:d}x{:d}".format(800, int(800/4*2)) )
            top.bind('<Escape>', lambda e: top.destroy())
        else:
            # in settingsPanel, set title
            l=Label(top, text="settings panel")
            l.grid(row=row, column=column, sticky=W)
            row+=2

        entries = []

        label = Label(top, text="")
        label.grid(row=row, column=column, sticky=W)
        row+=1
        label = Label(top, text="set time")
        label.grid(row=row, column=column, sticky=W)
        column+=1
        time = Entry(top)
        time.insert(0, self.sg.time)
        entries.append( { "object":self.sg, "name":"time", "value":time, "type":"f" } )
        time.grid(row=row, column=column, sticky=W)
        column=0
        row+=1
        label = Label(top, text="show coordinate system")
        label.grid(row=row, column=column, sticky=W)
        column+=1
        coord = IntVar()
        coord.set(self.sg.showCoordinateSystem)
        coordck = Checkbutton(top, variable=coord)
        entries.append( { "object":self.sg, "name":"showCoordinateSystem", "value":coord, "type":"b" } )
        coordck.grid(row=row, column=column, sticky=W)
        column=0
        row+=1
        label = Label(top, text="show bounding boxes")
        label.grid(row=row, column=column, sticky=W)
        column+=1
        bbs = IntVar()
        bbs.set(self.sg.showBoundingBox)
        bbsck = Checkbutton(top, variable=bbs)
        entries.append( { "object":self.sg, "name":"showBoundingBox", "value":bbs, "type":"b" } )
        bbsck.grid(row=row, column=column, sticky=W)
        column=0
        row+=1
        label = Label(top, text="show backside")
        label.grid(row=row, column=column, sticky=W)
        column+=1
        bs = IntVar()
        bs.set(self.sg.showBackside)
        bsck = Checkbutton(top, variable=bs)
        entries.append( { "object":self.sg, "name":"showBackside", "value":bs, "type":"b" } )
        bsck.grid(row=row, column=column, sticky=W)
        column=0
        row+=1

        column=0
        row+=1
        label = Label(top, text="polyOnlyGrid")
        label.grid(row=row, column=column, sticky=W)
        column+=1
        po = IntVar()
        po.set(self.sg.polyOnlyGrid)
        pock = Checkbutton(top, variable=po)
        entries.append( { "object":self.sg, "name":"polyOnlyGrid", "value":po, "type":"b" } )
        pock.grid(row=row, column=column, sticky=W)
        column=0
        row+=1

        button = Button(top, text="Apply", command=lambda: self.apply(top, entries, True))
        button.grid(row=row, column=column, sticky=W)
        column+=1
        button = Button(top, text="Cancel", command=lambda: self.apply(top, entries, False))
        button.grid(row=row, column=column, sticky=W)

    def apply(self, top, entries, apply=False):
        if apply is True:
            for entry in entries:
                value = entry["value"].get()
                if entry["type"]=="f":
                    value = float(value)
                elif entry["type"]=="b":
                    value = bool(value)
                elif entry["type"]=="s":
                    value = str(value)
                setattr(entry["object"], entry["name"], value)
                val=getattr(entry["object"], entry["name"])
                print("set", entry["name"], "to", val)
        if isinstance(top, Frame):
            self.settingsBrowser=False
        self.isShowing=True
        top.destroy()
        self.guiUpdate()

    def configureCanvas(self, event):
        self.width = self.winfo_width()-2
        self.height = self.winfo_height()-2
        print("TkGUI(): reinitialize camera width/height")
        self.cam.width = self.width
        self.cam.height = self.height
        pass

    def createViewer(self):
        canvas = Canvas(self.content)
        self.canvas = canvas
        self.content.bind("<Configure>", self.configureCanvas)
        canvas.pack(side=LEFT, fill=BOTH, expand=True)
        canvas.bind("<Button-1>", self.canvasInteract.start)
        canvas.bind("<Button-2>", self.canvasInteract.start)
        canvas.bind("<B1-Motion>", self.canvasInteract.rotate)
        canvas.bind("<B2-Motion>", self.canvasInteract.walk)
        canvas.bind("<ButtonRelease-1>", self.canvasInteract.release)
        canvas.bind("<ButtonRelease-2>", self.canvasInteract.release)
        pass