I'm creating a simple window manager for future projects and I seem to have run into a problem. I have a snippet of code which is supposed to change the viewport's position to the middle of the window whenever somebody resizes it, and it seems to work completely fine when changing position on the x-axis, as seen here. Unfortunately, it doesn't work on the y-axis, instead showing up at the bottom of the window. here is the code that handles this:
/* create viewport */
if (win->width > win->height)
glViewport((win->width / 2 - win->viewport.width / 2), 0, win->viewport.width, win->viewport.height);
else
/* FIXME: viewport appears at bottom of window, i have no idea why */
glViewport(0, (win->height / 2 - win->viewport.height / 2), win->viewport.width, win->viewport.height);
I have changed a number of variables in the equation but none of them yielded any results. I have ran the equation outside of glViewport and it returns the desired numbers. OpenGL is intentionally changing the viewports position to (0,0) and I have yet to figure out why. if it helps at all, I'm using OpenGL 3.3 and SDL2 on a Windows machine.
If anybody could tell me what I need to do to fix this, I would greatly appreciate it. Please and thank you.
I have run in a similar problem with SDL2 too.
I think the missing part is that you are not considering the aspect ractio value.
Also using SDL2 with Opengl you should consider that the drawable area can be different from the window area.
Assuming w and h the original sizes,
draw_w and _draw_h the current drawable area size,
view_w view_h the viewport area size,
we can calculate it as :
SDL_GL_GetDrawableSize(window,&draw_w,&draw_h);
float ratio = (float)w/(float)h;
if(draw_w/ratio < draw_h)
{
view_w = draw_w;
view_h = (int)((float)view_w/ratio);
}
else
{
view_h = draw_h;
view_w = (int)((float)view_h*ratio);
}
x = (draw_w-view_w)/2;
y = (draw_h-view_h)/2;
glViewport(x, y, view_w, view_h);
I have used a similar function applied to a SDL2 filter event as :
if(event->type==SDL_WINDOWEVENT && (event->window.event==SDL_WINDOWEVENT_SIZE_CHANGED || event->window.event==SDL_WINDOWEVENT_EXPOSED))
Related
I have two graphs of drawing signals on a gtkmm application.
The problem comes when I have to paint a graph with many points (around 300-350k) and lines to the following points since it slows down a lot to paint all the points each iteration.
bool DrawArea::on_draw(const Cairo::RefPtr<Cairo::Context>& c)
{
cairo_t* cr = c->cobj();
//xSignal.size() = ySignal.size() = 350000
for (int j = 0; j < xSignal.size() - 1; ++j)
{
cairo_move_to(cr, xSignal[j], ySignal[j]);
cairo_line_to(cr, xSignal[j + 1], ySignal[j + 1]);
}
cairo_stroke(cr);
return true;
}
I know that exist a cairo_stroke_preserve but i think is not valid for me because when I switch between graphs, it disappears.
I've been researching about save the path and restore it on the Cairo documentation but i donĀ“t see anything. In 2007, a user from Cairo suggested in the documentation 'to do' the same thing but apparently it has not been done.
Any suggestion?
It's not necessary that you draw everything in on_draw. What I understand from your post is that you have a real-time waveform drawing application where samples are available at fixed periods (every few milliseconds I presume). There are three approaches you can follow.
Approach 1
This is good particularly when you have limited memory and do not care about retaining the plot if window is resized or uncovered. Following could be the function that receives samples (one by one).
NOTE: Variables prefixed with m_ are class members.
void DrawingArea::PlotSample(int nSample)
{
Cairo::RefPtr <Cairo::Context> refCairoContext;
double dNewY;
//Get window's cairo context
refCairoContext = get_window()->create_cairo_context();
//TODO Scale and transform sample to new Y coordinate
dNewY = nSample;
//Clear area for new waveform segment
{
refCairoContext->rectangle(m_dPreviousX
+ 1,
m_dPreviousY,
ERASER_WIDTH,
get_allocated_height()); //See note below on m_dPreviousX + 1
refCairoContext->set_source_rgb(0,
0,
0);
refCairoContext->fill();
}
//Setup Cairo context for the trace
{
refCairoContext->set_source_rgb(1,
1,
1);
refCairoContext->set_antialias(Cairo::ANTIALIAS_SUBPIXEL); //This is up to you
refCairoContext->set_line_width(1); //It's 2 by default and better that way with anti-aliasing
}
//Add sub-path and stroke
refCairoContext->move_to(m_dPreviousX,
m_dPreviousY);
m_dPreviousX += m_dXStep;
refCairoContext->line_to(m_dPreviousX,
dNewY);
refCairoContext->stroke();
//Update coordinates
if (m_dPreviousX
>= get_allocated_width())
{
m_dPreviousX = 0;
}
m_dPreviousY = dNewY;
}
While clearing area the X coordinate has to be offset by 1 because otherwise the 'eraser' will clear of the anti-aliasing on the last coulmn and your trace will have jagged edges. It may need to be more than 1 depending on your line thickness.
Like I said before, with this method your trace will get cleared if the widget is resized or 'revealed'.
Approach 2
Even here the sample are plotted the same way as before. Only difference is that each sample received is pushed directly into a buffer. When the window is resized or 'reveled' the widget's on_draw is called and there you can plot all the samples one time. Of course you'll need some memory (quite a lot if you have 350K samples in queue) but the trace stays on screen no matter what.
Approach 3
This one also takes up a little bit of memory (probably much more depending on the size of you widget), and uses an off-screen buffer. Here instead of storing samples we store the rendered result. Override the widgets on_map method and on_size_allocate to create an offsceen buffer.
void DrawingArea::CreateOffscreenBuffer(void)
{
Glib::RefPtr <Gdk::Window> refWindow = get_window();
Gtk::Allocation oAllocation = get_allocation();
if (refWindow)
{
Cairo::RefPtr <Cairo::Context> refCairoContext;
m_refOffscreenSurface =
refWindow->create_similar_surface(Cairo::CONTENT_COLOR,
oAllocation.get_width(),
oAllocation.get_height());
refCairoContext = Cairo::Context::create(m_refOffscreenSurface);
//TODO paint the background (grids may be?)
}
}
Now when you receive samples, instead of drawing into the window directly draw into the off-screen surface. Then block copy the off screen surface by setting this surface as your window's cairo context's source and then draw a rectangle to draw the newly plotted sample. Also in your widget's on_draw just set this surface as the source of widget's cairo context and do a Cairo::Context::paint(). This approach is particularly useful if your widget probably doesn't get resized and the advantage is that the blitting (where you transfer contents of one surface to the other) is way faster than plotting individual line segments.
To answer your question:
There is cairo_copy_path() and cairo_append_path() (there is also cairo_copy_path_flat() and cairo_path_destroy()).
Thus, you can save a path with cairo_copy_path() and later append it to the current path with cairo_append_path().
To answer your not-question:
I doubt that this will speed up your drawing. Appending these lines to the current path is unlikely to be slow. Rather, I would expect the actual drawing of these lines to be slow.
You write "it slows down a lot to paint all the points each iteration.". I am not sure what "each iteration" refers to, but why are you drawing all these points all the time? Wouldn't it make more sense to only draw them once and then to re-use the drawn result?
I have been stuck on remapping my mouse position to a new frame for a few days and I am unsure what to do. I will provide images to describe my issue. The main problem is I want to click on an object in my program and the program will highlight the object I select (in 3d space.) I have this working perfectly when my application is in full screen mode. I recently started rendering my scene into a smaller frame so that I can have editor tools on the sides(like unity.) Here is the transition (graphically) i made from working to not working:
So essentially the mouse coordinates go from (0,0) to (screenWidth, screenHeight).. I want to map these coordinates to be from (frameStartX, frameStartY) to (frameStartX + frameWidth, frameStartY + frameHeight). I did some research on linearly transforming a number to scale it to a new range so I thought I could do this:
float frameMousePosX = (mousePosX - 0) / (screenWidth - 0) * ((frameWidth + frameStartX) - frameStartX ) + frameStartX ;
float frameMousePosY = (mousePosY - 0) / (screenHeight - 0) * ((frameHeight +frameStartY) - frameStartY ) + frameStartY;
I assumed this would work but it doesn't. It's not even close. I am really unsure what to do to get this transformation.
Assuming the transformation works, I would want it to read 0,0 at the bottom left of the frameStart which is (x,y) in the image attached and then reach its peak at the top right of the framed scene.
Any help would be extremely appreciated.
i've been looking around to find the best way to have wrap arround edges of a world for the bodies.
I managed to use this topic to do it :
How do I make a Box2D wrap around world?
Using SetTransform() i could make it appear on the other X/Z side.
Now Let's say i have an object for example a simple box 10x10.
If half the box goes beyond upper Y edge i want the portion that goes beyond to appear below with the other part of the box that is still visibile on the upper part to stay there.
To summarize i want a "real wrap arround edge like this used to be done in old games.
I hope i was clear enough...
Edit :
I've added a picture to explain what i mean :
Thanks
I have not used Box2d but I have resolved this problem before in Processing, hopefully the logic translates easily
In Processing a rectangle is drawn as rect(x, y, rectangleWidth, rectangleHeight) at position x, y which represents the top left corner of the rectangle. The rectangle's width and height point right and down from x and y respectively
The idea is to draw the rectangle normally unless the bottom would be off the bottom edge of the viewport (because the rectangle's height points down based on y). If the rectangle is off the bottom edge of the viewport then you instead draw two partial rectangles
In the code below height is the height of your viewport
if (y < height-10)
rect(x, y, 10, 10); // Normal condition, entire rectangle
else {
rect(x, 0, 10, 10-(height-y)); // Top partial rectangle
rect(x, y, 10, height-y); // Bottom partial rectangle
}
Finally i googled a bit found this article on Unity :
http://gamedevelopment.tutsplus.com/articles/create-an-asteroids-like-screen-wrapping-effect-with-unity--gamedev-15055
On Box2D I created 8 ghosts bodies which were positionned as defined in the article.
During Box2D Steps i added some logics to check where my original body is positionned.
When it goes to the edges the Ghost Body appears on the other edge. It works also when the original Body goes on a corder. 4 ghosts bodies will appear on each edges.
I want to draw a triangle and text using C++ and Cairo like this:
|\
| \
|PP\
|___\
If I add the triangle and the text using Cairo I get:
___
| /
|PP/
| /
|/
So the y-axis is from top to bottom, but I want it from bottom to top. So I tried to changed the viewpoint matrix (cairo_transform(p, &mat);) or scale the data (cairo_scale(p, 1.0, -1.0);). I get:
|\
| \
|bb\
|___\
Now the triangle is the way I want it BUT the TEXT is MIRRORED, which I do not want to be mirrored.
Any idea how to handle this problem?
I was in a similar situation as the OP that required me to change a variety of coordinates in the cartesian coordinate system with the origin at the bottom left. (I had to port an old video game that was developed with a coordinate system different from Cairo's, and because of time constraints/possible calculation mistakes/port precision I decided it was better to not rewrite the whole bunch) Luckily, I found an okay approach to change Cairo's coordinate system. The approach is based around Cairo's internal transformation matrix, that transforms Cairo's input to the user device. The solution was to change this matrix to a reflection matrix, a matrix that mirrors it's input through the x-axis, like so:
cairo_t *cr;
cairo_matrix_t x_reflection_matrix;
cairo_matrix_init_identity(&x_reflection_matrix); // could not find a oneliner
/* reflection through the x axis equals the identity matrix with the bottom
left value negated */
x_reflection_matrix.yy = -1.0;
cairo_set_matrix(cr, &x_reflection_matrix);
// This would result in your drawing being done on top of the destination
// surface, so we translate the surface down the full height
cairo_translate(cr, 0, SURFACE_HEIGHT); // replace SURFACE_HEIGHT
// ... do your drawing
There is one catch however: text will also get mirrored. To solve this, one could alter the font transformation matrix. The required code for this would be:
cairo_matrix_t font_reflection_matrix;
// We first set the size, and then change it to a reflection matrix
cairo_set_font_size(cr, YOUR_SIZE);
cairo_get_font_matrix(cr, &font_reflection_matrix);
// reverse mirror the font drawing matrix
font_reflection_matrix.yy = font_reflection_matrix.yy * -1;
cairo_set_font_matrix(cr, &font_reflection_matrix);
Answer:
Rethink your coordinates and pass them correctly to cairo. If your coordinates source has an inverted axis, preprocess them to flip the geometry. That would be called glue code, and it is ofter neccessary.
Stuff:
It is a very common thing with 2D computer graphics to have the origin (0,0) in the top left corner and the y-axis heading downwards (see gimp/photoshop, positioning in html, webgl canvas). As allways there are other examples too (PDFs).
I'm not sure what the reason is, but I would assume the reading direction on paper (from top to bottom) and/or the process of rendering/drawing an image on a screen.
To me, it seems to be the easiest way to procedurally draw an image at some position from the first to the last pixel (you don't need to precalculate it's size).
I don't think that you are alone with your oppinion. But I don't think that there is a standard math coordinate system. Even the very common carthesian coordinate system is incomplete when the arrows that indicate axis direction are missing.
Summary: From the discussion I assume that there is only one coordinate system used by Cairo: x-axis to the right, y-axis down. If one needs a standard math coordinate system (x-axis to the right, y-axis up) one has to preprocess the data.
It seems that this is quite a common question, but I can't find a person with my same circumstances. The closest seems to be: OpenGL: scale then translate? and how?.
The problem I'd really like some help with is to do with moving around while zoomed into (and out of) a 2d scene using OpenGl. The code for zooming out is pretty simple:
void RefMapGLScene::zoomOut(){
currentScale = currentScale-zoomFactor;
double xAdjust = (((get_width())*zoomFactor/2));
double yAdjust = ((get_height()*zoomFactor/2));
zoomTranslateX -= xAdjust;
zoomTranslateY -= yAdjust;
}
The code for zooming in is basically the same (add the zoomFactor to currentScale, and increment zoomTranslateX and Y).
The code for rending everything is also simple:
glPushMatrix();
glTranslated(-zoomTranslateX, -zoomTranslateY, 0);
glScaled(currentScale, currentScale, 1);
glTranslated(totalMovedX, totalMovedY, 0);
graph->draw();
glPopMatrix();
Essentially, zoomTranslate stores an adjustment needed to make the screen move a little towards the middle when zooming. I don't do anything nice like move to where the mouse is pointing, I just move to the middle (ie, to the right and up/down depending on your co-ordinate system). TotalMovedX and Y store the mouse movement as follows:
if (parent->rightButtonDown){
totalMovedX += (-(mousex-curx))/currentScale;
totalMovedY += (-(mousey-cury))/currentScale;
}
Dragging while not zoomed in or out works great. Zooming works great. Dragging while zoomed in/out does not work great :) Essentially, when zoomed in, the canvas moves a lot slower than the mouse. The opposite for when zoomed out.
I've tried everything I can think of, and have read a lot of this site about people with similar issues. I also tried reimplementing my code using glOrtho to handle the zooms, but ended up facing other problems, so came back to this way. Could anybody please suggest how I handle these dragging events?
The order of operations matter. Operations on matrices are applied in the reverse order in which you multiplied the matrices. In your case you apply the canvas movement before the scaling, so your mouse drag is also zoomed.
Change your code to this:
glPushMatrix();
glTranslated(-zoomTranslateX, -zoomTranslateY, 0);
glTranslated(totalMovedX, totalMovedY, 0);
glScaled(currentScale, currentScale, 1);
graph->draw();
glPopMatrix();
Also after changing that order you don't have to scale your mouse moves, so you can omit that division by currentScale
if (parent->rightButtonDown){
totalMovedX += (-(mousex-curx));
totalMovedY += (-(mousey-cury));
}