"stroke" required after "showing" Pango layout? - python

I have been chasing a problem between PyCairo and PangoCairo. The following code illustrates it:
import math
import cairo
import gi
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Pango, PangoCairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 400, 400)
ctx = cairo.Context(surface)
# TOP LEFT CIRCLE
ctx.save()
ctx.arc(100.0, 100.0, 50, 0, 2 * math.pi)
ctx.set_source_rgba(0.0, 0.0, 1.0, 1.0)
ctx.set_line_width(2.0)
ctx.stroke()
ctx.restore()
# CENTER TEXT
ctx.save()
layout = PangoCairo.create_layout(ctx)
layout.set_font_description(Pango.font_description_from_string('Arial 10.0'))
layout.set_markup('<b>Foo Bar</b>', -1)
ctx.set_source_rgba(0.0, 1.0, 0.0, 1.0)
_, text_extents = layout.get_pixel_extents()
text_width, text_height = text_extents.width, text_extents.height
ctx.translate(200.0, 200.0)
ctx.translate(-text_width / 2, -text_height / 2)
ctx.move_to(0.0, 0.0)
PangoCairo.show_layout(ctx, layout)
# ctx.stroke() # WHY?
ctx.restore()
# BOTTOM RIGHT CIRCLE
ctx.save()
ctx.arc(300.0, 300.0, 50, 0, 2 * math.pi)
ctx.set_source_rgba(1.0, 0.0, 0.0, 1.0)
ctx.set_line_width(2.0)
ctx.stroke()
ctx.restore()
surface.write_to_png('test.png')
It results in the following picture:
My intention is to draw two circles and text. The line between the text and the bottom right circle is not intended to exist. I can make the line disappear by adding / uncommenting the ctx.stroke() call directly underneath PangoCairo.show_layout in the center text code block.
It works, but it does not feel right. The text does not require a line stroke. What is going wrong? Is the stroke actually required or have I made another mistake?

Try ctx.new_path() instead of ctx.stroke().
Showing the layout seems to set the current point and thus the following line_to that arc does implicitly at its beginning actually shows a line.

Related

Unwanted cutting problem in visualization

I’m newbie at VTK.
When I try to visualize a sphere, and I adjust z value in sphere.SetCenter(0.0, 0.0, z_value), I only can visualize cut sphere.
I guess there is a viewing frame(:cube). How can I extend my viewing cube?
half sphere
z_value = -1.0
ren = vtk.vtkRenderer()
sphere = vtk.vtkSphere()
sphere.SetCenter(0.0, 0.0, z_value)
sphere.SetRadius(0.1)
# The sample function generates a distance function from the implicit
# function. This is then contoured to get a polygonal surface.
sample = vtk.vtkSampleFunction()
sample.SetImplicitFunction(sphere)
#sample.SetModelBounds(-100.0, 100.0, -100.0, 100.0, -100.0, 100.0)
#sample.SetSampleDimensions(20, 20, 20)
sample.ComputeNormalsOff()
# contour
surface = vtk.vtkContourFilter()
surface.SetInputConnection(sample.GetOutputPort())
surface.SetValue(0, 0.0)
# mapper
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(surface.GetOutputPort())
mapper.ScalarVisibilityOff()
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetEdgeColor(.2, .2, .5)
# A renderer and render window
ren = vtk.vtkRenderer()
ren.SetBackground(0, 0, 0)
# add the actor
ren.AddActor(actor)
renWin = vtk.vtkRenderWindow()
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
renWin.AddRenderer(ren)
# This allows the interactor to initalize itself. It has to be
# called before an event loop.
iren.Initialize()
# We'll zoom in a little by accessing the camera and invoking a "Zoom"
# method on it.
ren.ResetCamera()
#ren.GetActiveCamera().Zoom(1.5)
renWin.Render()
# Start the event loop.
iren.Start()
To fix your script, you should uncomment the sample.SetModelBounds() line and set the actual bounds of your sphere, i.e.
sample.SetModelBounds(centerX - radius, centerX + radius, centerY - radius, centerY + radius, centerZ - radius, centerZ + radius,)
That said, if you only want to display a sphere, you've better to use the vtkSphereSource. So you can replace the sphere-sample-contour part by :
sphere = vtk.vtkSphereSource()
sphere.SetCenter(0.0, 0.0, z_value)
sphere.SetRadius(0.1)
sphere.Update()
and below
mapper.SetInputConnection(sphere.GetOutputPort())

OpenGL drawn area occupies only lower left quadrant of the available window

I only just started with OpenGL and PyOpenGL and am using tutorial code from this page https://noobtuts.com/python/opengl-introduction. However I quickly ran into the following problem: while the code successfully draws what is intended the drawing cannot occupy more than the lower left quadrant of my window. E.g. in the below I am setting the size and position of the rectangle such that it occupies the full window, as you can see in the code below I set the rectangle’s width and height to the window’s width and height and the position is 0,0 so I would expect the full window to become blue but this is not happening as you can see below. I am on Mac OS Catalina and running PyOpenGL on Python 3.
I have seen elsewhere that there is something to do with Catalina at this place: https://github.com/redeclipse/base/issues/920
and this place https://github.com/ioquake/ioq3/issues/422#issuecomment-541193050
However this is way too advanced for me to understand.
Would anyone know how that can be solved perhaps?
Thanks ahead for your help
from OpenGL import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
window = 0 # glut window number
width, height = 500, 400 # window size
def refresh2d(width, height):
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, width, 0.0, height, 0.0, 1.0)
glMatrixMode (GL_MODELVIEW)
glLoadIdentity()
def draw_rect(x, y, width, height):
glBegin(GL_QUADS) # start drawing a rectangle
glVertex2f(x, y) # bottom left point
glVertex2f(x + width, y) # bottom right point
glVertex2f(x + width, y + height) # top right point
glVertex2f(x, y + height) # top left point
glEnd() # done drawing a rectangle
def draw(): # ondraw is called all the time
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # clear the screen
glLoadIdentity() # reset position
refresh2d(width, height) # set mode to 2d
glColor3f(0.0, 0.0, 1.0) # set color to blue
draw_rect(0, 0, 500, 400) # rect at (0, 0) with width 500, height 400
glutSwapBuffers() # important for double buffering
# initialization
glutInit() # initialize glut
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(width, height) # set window size
glutInitWindowPosition(0, 0) # set window position
window = glutCreateWindow("my first attempt") # create window with title
glutDisplayFunc(draw) # set draw function callback
glutIdleFunc(draw) # draw all the time
glutMainLoop() # start everything
However this isn't working. I am definitely getting a window where the blue rectangle occupies only the lower left quadrant.
FWIW, using glfw I'm able to get around this problem:
width = 1280
height = 1024
win = glfw.CreateWindow(width, height, "window title")
fb_width, fb_height = glfw.GetFramebufferSize(win)
glViewport(0, 0, fb_width, fb_height) # <--- this is the key line
you can install modified glut http://iihm.imag.fr/blanch/software/glut-macosx/
or you can do
glViewport(0, 0, width*2, height*2)
if you don't care about DPI

How can I create a cylinder linking two points with Python withour using blender's bpy?

I want to position a cylinder such that the center of one end is at (x0, y0, z0) and the center of the other end is at (x1, y1, z1) using the pure Python API.
I want to attain this using Python OpenGL or any other GL libs but not blender lib
My Problem
I searched in stackoverflow and found this question
https://blender.stackexchange.com/questions/5898/how-can-i-create-a-cylinder-linking-two-points-with-python
But it is using blender and I don't want it
I need to run the python script from command line or pycharm ide
So bpy module can't work outside blender
I want a function like this format
cylinder_between(x1, y1, z1, x2, y2, z2, rad):
The function have to display a cylinder
Please guide me
2) also suggest how to clear the screen fully after drawing the
cylinder
If you want to use modern OpenGL then you've to generate a Vertex Array Object and to write a Shader.
A solution with less lines of code is to use Legacy OpenGL and either gluCylinder or glutSolidCylinder.
With this (deprecated) tool set a cylinder with few lines of code can be drawn e.g.:
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
import math
def cross(a, b):
return [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]]
def cylinder_between(x1, y1, z1, x2, y2, z2, rad):
v = [x2-x1, y2-y1, z2-z1]
height = math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
axis = (1, 0, 0) if math.hypot(v[0], v[1]) < 0.001 else cross(v, (0, 0, 1))
angle = -math.atan2(math.hypot(v[0], v[1]), v[2])*180/math.pi
glPushMatrix()
glTranslate(x1, y1, z1)
glRotate(angle, *axis)
glutSolidCylinder(rad, height, 32, 16)
glPopMatrix()
def draw():
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, wnd_w/wnd_h, 0.1, 10)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(0, -2, 0, 0, 0, 0, 0, 0, 1)
glClearColor(0.5, 0.5, 0.5, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# glEnable(GL_DEPTH_TEST)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) # causes wire frame
glColor(1, 1, 0.5)
cylinder_between(0.2, 0.4, -0.5, -0.2, -0.4, 0.5, 0.3)
glutSwapBuffers()
glutPostRedisplay()
wnd_w, wnd_h = 300, 300
glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(wnd_w, wnd_h)
glutInitWindowPosition(50, 50)
glutCreateWindow("cylinder")
glutDisplayFunc(draw)
glutMainLoop()
See also Immediate mode and legacy OpenGL
Is there any way to implement manually do pan , rotate , tilt ,move the viewport like we do in 3D modeling software using mouse (that alt+mmb )
See:
How to implement camera pan like in 3dsMax?
Proper way to handle camera rotations
How to implement zoom towards mouse like in 3dsMax?

How to set clipping planes with opengl and pyglet

I am troubleshooting a problem with my code that if the depth value of any primitive is not zero it will not render on the screen. I suspect that it gets clipped away.
Is there an easy pythonic way to set my clipping planes in pyglet ?
This is my code so far:
import pyglet
from pyglet.gl import *
import pywavefront
from camera import FirstPersonCamera
def drawloop(win,camera):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#glClearColor(255,255,255,255)
glLoadIdentity()
camera.draw()
pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
('v3f', (10.0, 15.0, 0.0, 30.0, 35.0, 150.0))
)
glPointSize(20.)
return pyglet.event.EVENT_HANDLED
def main():
win = pyglet.window.Window()
win.set_exclusive_mouse(True)
win.clear()
camera = FirstPersonCamera(win)
#win.event
def on_draw():
drawloop(win,camera)
def on_update(delta_time):
camera.update(delta_time)
pyglet.clock.schedule(on_update)
pyglet.app.run()
if __name__ == '__main__':
main()
I am using the FirstPersonCamera snippet from here:
https://gist.github.com/mr-linch/f6dacd2a069887a47fbc
I am troubleshooting a problem with my code that if the depth value of any primitive is not zero it will not render on the screen. I suspect that it gets clipped away.
You have to set up a projection matrix to solve the issue. Either set up an orthographic projection matrix or a perspective projection matrix.
The projection matrix describes the mapping from 3D points of the view on a scene, to 2D points on the viewport. It transforms from eye space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates. The NDC are in range (-1,-1,-1) to (1,1,1). Every geometry which is out of the clippspace is clipped.
At Orthographic Projection the coordinates in the view space are linearly mapped to clip space coordinates and the clip space coordinates are equal to the normalized device coordinates, because the w component is 1 (for a cartesian input coordinate).
The values for left, right, bottom, top, near and far define a box. All the geometry which is inside the volume of the box is "visible" on the viewport.
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport. The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
To set a projection matrix the projection matrix stack has to be selected by glMatrixMode.
An orthographic projection can be set by glOrhto:
w, h = 640, 480 # default pyglet window size
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho( -w/2, w/2, -h/2, h/2, -1000.0, 1000.0) # [near, far] = [-1000, 1000]
glMatrixMode(GL_MODELVIEW)
....
An perspective projection can be set by gluPerspective:
w, h = 640, 480 # default pyglet window size
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective( 90.0, 640.0/480, 0.1, 1000.0) # fov = 90 degrees; [near, far] = [0.1, 1000]
glMatrixMode(GL_MODELVIEW)
....
I recommend to use the following coordinates, to "see" the points in both of the above cases:
e.g.:
pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
('v3f', (-50.0, -20.0, -200.0, 40.0, 20.0, -250.0)))
glPointSize(20.0)

openGL/Pyglet texture graphics vanish when drawing sprites

My code draws a 3D world, with a 2D set of graphics on top of it. The 3D world is made out of textured quads and the textures are generated with the following code:
textures = []
image = pyglet.image.load(os.path.join(img_dir, "magic.png"))
textures.append(image.get_texture())
glEnable(textures[-1].target)
glBindTexture(textures[-1].target, textures[-1].id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height,
0, GL_RGBA, GL_UNSIGNED_BYTE,
image.get_image_data().get_data('RGBA',
image.width * 4))
The quads are then drawn with (the other 2 just have different coords):
glBindTexture(texture.target, texture.id)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0); glVertex3f(4.0, -2.0, 100.0+self.clock)
glTexCoord2f(1.0, 0.0); glVertex3f(4.0, -2.0, -100.0+self.clock)
glTexCoord2f(1.0, 1.0); glVertex3f(-4.0, -2.0, -100.0+self.clock)
glTexCoord2f(0.0, 1.0); glVertex3f(-4.0, -2.0, 100.0+self.clock)
glEnd()
I have set up the correct parameters when drawing the 3D and 2D graphics, and when I draw a 2D triangle on top of the 3D quad (with the following code) everything works fine:
glBegin(GL_TRIANGLES)
glVertex3f(0.0, 10, 0.0)
glVertex3f(-10, -10, 0)
glVertex3f(10, -10, 0)
glEnd()
However, I then try to draw a sprite and the 3D quads lose their texture and are drawn as white.
self.spr=pyglet.sprite.Sprite(pyglet.image.load(os.path.join(img_dir, "largebullet.png")).get_texture())
...
self.spr.draw()
Note that there's some fog in the background
I found a solution to this, by running glDisable(texture.target) on the enabled textures after they were drawn. It's not ideal because they have to be reenabled again, but for now it works ok.
I ran into a similar problem to this, and I found that the pyglet Sprite class tends to disable everything in the OpenGL state that it sets. You must reset a lot of things each time any sprite is drawn.

Categories