I am drawing a graph using cairo in gtk of C language. I used cairo_show_text to show text. It is easy to define the center point for center-align. But I didn't know how to make the text center-align.
The problem is that I don't know how to calculate text length(It is also concerning about font size I think). If I can get the text legth, I can move to the appropriate point by using cairo_move_to and then show the text by cairo_show_text.
Any suggestion or any other approach?
According to the followed liberforce's comment, the solution is
/* howto_move: 0 -- cairo_move_to, 1 -- cairo_rel_move_to
* x, y is the coordinate of the point for center-align. Whether it is absolute
* or relative coordinate depends on `howto_move'
*/
void cairo_text_align_horizontal_center (cairo_t *cr, char *text, int if_vertical, int howto_move, double x, double y)
{
cairo_text_extents_t te;
double new_x, new_y;
cairo_text_extents(cr, text, &te);
if(!if_vertical)
{
new_x = x - (te.x_bearing + te.width / 2);
new_y = y;
}
else
{
new_x = x;
new_y = y + (te.x_bearing + te.width / 2);
}
if(howto_move == 0)
cairo_move_to(cr, new_x, new_y);
else
cairo_rel_move_to(cr, new_x, new_y);
cairo_save(cr);
if(!if_vertical)
cairo_rotate(cr, 0);
else
cairo_rotate(cr, - PI / 2.0);
cairo_show_text(cr, text);
cairo_restore(cr);
cairo_stroke(cr);
}
Look at the cairo tutorial text alignment section.
Keep in mind that for text, the Cairo API is a bit limited. For more advanced stuff, you'll need pangocairo.
Related
I'm new to shaders and I have been messing about with the website shadertoy. I'm trying to understand graphics (and the graphics pipeline) such as drawing lines, interpolation, rasterization, etc... I've written two line functions that return a color if the pixel processed is on the line. This is the shadertoy code here using fragment shaders
struct Vertex {
vec2 p;
vec4 c;
};
vec4 overlay(vec4 c1, vec4 c2) {
return vec4((1.0 - c2.w) * c1.xyz + c2.w * c2.xyz, 1.0);
}
vec4 drawLineA(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 r = floor(pos);
vec2 diff = b - a;
if (abs(diff.y) < abs(diff.x)) {
if (diff.x < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.y / diff.x;
float q = r.x - a.x;
if (floor(m * q + a.y) == r.y && a.x <= r.x && r.x <= b.x) {
float h = q / diff.x;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
} else {
if (diff.y < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.x / diff.y;
float q = r.y - a.y;
if (floor(m * q + a.x) == r.x && a.y <= r.y && r.y <= b.y) {
float h = q / diff.y;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
}
return vec4(0,0,0,0);
}
vec4 drawLineB(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 l = b - a;
vec2 r = pos - a;
float h = dot(l,r) / dot (l,l);
vec2 eC = a + h * l;
if (floor(pos) == floor(eC) && 0.0 <= h && h <= 1.0 ) {
return vec4((1.0 - h) * v1.c + h * v2.c);
}
return vec4(0,0,0,0);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float t = iTime;
float r = 300.0;
Vertex v1 = Vertex(vec2(400,225), vec4(1,0,0,1));
Vertex v2 = Vertex(vec2(400.0 + r*cos(t) ,225.0 + r*sin(t)), vec4(0,1,0,1));
vec4 col = vec4(0,0,0,1);
col = overlay(col,drawLineA(v1, v2, fragCoord));
col = overlay(col,drawLineB(v1, v2, fragCoord));
// Output to screen
fragColor = col;
}
However, the lines that I have been using are not fast or using antialiasing. Which is the fastest algorithm for both antialiasing and aliasing lines, and how should I implement it thanks.
A fragment shader is really not the right approach for this, a lot on shadertoy is really just a toy / code-golfing showing solutions overcoming the limitations of the platform which are terribly inefficient in real-world scenarios.
All graphics APIs provide dedicated interfaces for drawing line segments just search for "API_NAME draw line" e.g. "webgl draw line". In cases where those do not suffice triangle strips with either MSAA or custom in-shader AA are used.
If you're really just looking for an efficient algorithm the wikipedia page has you covered on that.
As the other answer says shaders are not very good for this.
Line rasterization is done behind the scenes with HW interpolators on the gfx card these days. The shaders are invoked for each pixel of rendered primitive which in your case means its called for every pixel of screen and this all is invoked for each line you render which is massively slower than native way.
If you truly want to learn rasterization do this on CPU side instead. The best algo for lines depends on the computation HW architecture you are using.
For sequentional processing it is:
DDA this one is with subpixel precision
In the past Bresenham was faster but that is not true IIRC since x386 ...
For parallel processing you just compute distance of pixel to the line (more or less like you do now).
So if you insist on using shaders for this You can speed up things using geometry shader and process only fragment (pixels) that are near your line. See:
cubic curves rendering in GLSL
So simply you create OOBB around your line and render it by emitting 2 triangles per line then in fragment you compute the distance to line and set the color accordingly ...
For antialiasing you simply change the color for pixels on the last pixel edge distance. So if your line has half width w and distance of fragment to line is d then:
if (d>w) discard; // fragment too far
d=(w-d)/pixel_size; // distance from edge in pixels
frag_color = vec4(r,g,b,min(1.0,d)); // use transparency/blending
As you can see anti aliasing is just rendering with blending modulated by subpixel position/distance of pixel relative to rasterized object) the same technique can be used with DDA.
There are also ray tracing methods of rendering lines but they are pretty much the same as finding distance to line ... however instead of 2D pixel position you checking against 3D ray which slightly complicates the math.
I am using PCL viewer (which uses VTK) for visualizing a 3D point cloud generated by SLAM algorithm. I am trying to render the view of point cloud as seen by the robot at a given pose (position and orientation). I am able to set the position and ViewUp vector of the camera, but I am unable to set the Focal point of the camera to the heading of the robot. Currently, I am using sliders to set the Focal Point, but I want to set it programmatically based on the heading.
I am trying understand the type (angle in rad / distance in m) and range of values VTKCamera Focal Point expects and how that's related to the heading.
Function where I am updating camera
void Widget::setcamView(){
//transfrom position
Eigen::Vector3d position = this->transformpose(Eigen::Vector3d(image_pose.at(pose_ittr).position[0], image_pose.at(pose_ittr).position[1], image_pose.at(pose_ittr).position[2]));
posx = position(0);
posy = position(1);
posz = position(2);
//transform the pose
Eigen::Vector3d attitude = this->transformpose(Eigen::Vector3d(image_pose.at(pose_ittr).orientation[0],image_pose.at(pose_ittr).orientation[1],image_pose.at(pose_ittr).orientation[2]));
roll = attitude(0);
pitch = attitude(1);
yaw = attitude(2);
viewx = ui->viewxhSlider->value();// * std::pow(10,-3);
viewy = ui->viewyhSlider->value();// * std::pow(10,-3);
viewz = ui->viewzhSlider->value();// * std::pow(10,-3);
// debug
std::cout<<"Positon: "<<posx<<"\t"<<posy<<"\t"<<posz<<std::endl<<
"View: "<<viewx<<"\t"<<viewy<<"\t"<<viewz<<std::endl<<
"Orientation: "<<roll<<"\t"<<pitch<<"\t"<<yaw<<std::endl;
point_cutoffy = ui->ptcutoffhSlider->value();
if(yaw <=0)
yaw = yaw * -1;
viewer->setCameraPosition(posx,posy,posz+1,
viewz,viewy,viewz,
0, 0, 1, 0);
viewer->setCameraFieldOfView(1);
viewer->setCameraClipDistances(point_cutoffx,point_cutoffy,0);
ui->qvtkWidget->update();
count++;
}
Any help is greatly appreciated.
-Thanks
P.S
PCL Viewer Set Camera Implementation (uses VTK)
void pcl::visualization::PCLVisualizer::setCameraPosition (
double pos_x, double pos_y, double pos_z,
double view_x, double view_y, double view_z,
double up_x, double up_y, double up_z,
int viewport)
{
rens_->InitTraversal ();
vtkRenderer* renderer = NULL;
int i = 0;
while ((renderer = rens_->GetNextItem ()) != NULL)
{
// Modify all renderer's cameras
if (viewport == 0 || viewport == i)
{
vtkSmartPointer<vtkCamera> cam = renderer->GetActiveCamera ();
cam->SetPosition (pos_x, pos_y, pos_z);
cam->SetFocalPoint (view_x, view_y, view_z);
cam->SetViewUp (up_x, up_y, up_z);
renderer->ResetCameraClippingRange ();
}
++i;
}
win_->Render ();
}
I'm working with very similar problem via opencv Viz, which also uses VTK. Relatively to your question, I think you can find an answer HERE
I am trying to simulate a mouse click on the CView window in a legacy code which I must say I don't fully understand. The idea is to search for a particular item in the CView, get its co-ordinates and then simulate a right mouse click on it using SendInput. I want to understand if the basic steps I am following are correct before I proceed digging further into the legacy code which has a bunch of transformations happening across co-ordinate systems :( Here are the steps I follow:
Get the position co-ordinates of the item displayed in CView. at this point the co-ordinates is in the internal co-ordinate system (lets call it CDPoint).
CDPoint gPosn = viewObj->m_point_a ;
Covert the co-ordinates to the client co-ordinate system i.e CDPoint to CPoint using the existing transformations in the code.
CPoint newPosn = GetTransform().Scale(gPosn);
//Note: The basis of arriving that this is the correct transformation to use is the below code with the exact reverse transform happening in the mouse click handler code to convert CPoint to CDPoint:
`CDesignView::OnLButtonDown(UINT nFlags, CPoint p) {
CDPoint np = GetTransform().DeScale(p);
}`
Is this thinking right that CPoint received in the OnLButtonDown() handler will always be in the client co-ordinates and hence the reverse transform should convert CDPoint (internal co-ordinates) to client coordinates (CPoint) ?
Convert client co-ordinates to screen co-ordinates:
ClientToScreen(&newPosn);
Pass these values to SendInput function after converting to pixel co-ordinates:
INPUT buffer[1];
MouseSetup(buffer);
MouseMoveAbsolute(buffer, newPos.x, newPos.y);
MouseClick(buffer);
The Mousexxx() functions are defined as below similar to the sample code in this post:
How to simulate a mouse movement
.
#define SCREEN_WIDTH (::GetSystemMetrics( SM_CXSCREEN )-1)
#define SCREEN_HEIGHT (::GetSystemMetrics( SM_CYSCREEN )-1)
static void inline makeAbsXY(double &x, double &y) {
x = (x * 0xFFFF) / SCREEN_WIDTH ;
y = (y * 0xFFFF) / SCREEN_HEIGHT ;
}
static void inline MouseSetup(INPUT *buffer)
{
buffer->type = INPUT_MOUSE;
buffer->mi.dx = (0 * (0xFFFF / SCREEN_WIDTH));
buffer->mi.dy = (0 * (0xFFFF / SCREEN_HEIGHT));
buffer->mi.mouseData = 0;
buffer->mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
buffer->mi.time = 0;
buffer->mi.dwExtraInfo = 0;
}
static void inline MouseMoveAbsolute(INPUT *buffer, double x, double y)
{
makeAbsXY(x,y) ;
buffer->mi.dx = x ;
buffer->mi.dy = y ;
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);
SendInput(1, buffer, sizeof(INPUT));
}
static void inline MouseClick(INPUT *buffer)
{
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN);
SendInput(1, buffer, sizeof(INPUT));
Sleep(10);
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP);
SendInput(1, buffer, sizeof(INPUT));
}
Could anyone pls provide pointers on what might be going wrong in these steps since the simulated mosue click always seem to be shifted left by some factor which keeps increasing as x becoems larger. I have verified that is gPosn is pointing to (0,0) it always simulates a mouse click on the top right corner of the client screen.
Thanks for your time.
If you have x and y in client coordinates, you have to convert them to screen coordinates:
POINT point;
point.x = x;
point.y = y;
::ClientToScreen(m_hWnd, point);
Where m_hWnd is the window which owns the objects. x and y are relative to top-left of the client area of this window.
Assuming point.x and point.y are in screen coordinates, the rest of the conversion for SendInput is correct. You can also create INPUT array for SendInput, this will send the mouse messages without interruption.
INPUT input[3];
for (int i = 0; i < 3; i++)
{
memset(&input[i], 0, sizeof(INPUT));
input[i].type = INPUT_MOUSE;
}
input[0].mi.dx = (point.x * 0xFFFF) / (GetSystemMetrics(SM_CXSCREEN) - 1);
input[0].mi.dy = (point.y * 0xFFFF) / (GetSystemMetrics(SM_CYSCREEN) - 1);
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
SendInput(3, input, sizeof(INPUT));
I am working with OpenGL ES 2.0 on an Android device.
I am trying to get a sphere up and running and drawing. Currentley, I almost have a sphere, but clearly it's being done very, very wrong.
In my app, I hold a list of Vector3's, which I convert to a ByteBuffer along the way, and pass to OpenGL.
I know my code is okay, since I have a Cube and Tetrahedron drawing nicley.
What two parts I changed were:
Determing the vertices
Drawing the vertices.
Here are the code snippits in question. What am I doing wrong?
Determining the polar coordinates:
private void ConstructPositionVertices()
{
for (float latitutde = 0.0f; latitutde < (float)(Math.PI * 2.0f); latitutde += 0.1f)
{
for (float longitude = 0.0f; longitude < (float)(2.0f * Math.PI); longitude += 0.1f)
{
mPositionVertices.add(ConvertFromSphericalToCartesian(1.0f, latitutde, longitude));
}
}
}
Converting from Polar to Cartesian:
public static Vector3 ConvertFromSphericalToCartesian(float inLength, float inPhi, float inTheta)
{
float x = inLength * (float)(Math.sin(inPhi) * Math.cos(inTheta));
float y = inLength * (float)(Math.sin(inPhi) * Math.sin(inTheta));
float z = inLength * (float)Math.cos(inTheta);
Vector3 convertedVector = new Vector3(x, y, z);
return convertedVector;
}
Drawing the circle:
inGL.glDrawArrays(GL10.GL_TRIANGLES, 0, numVertices);
Obviously I omitted some code, but I am positive my mistake lies in these snippits somewhere.
I do nothing more with the points than pass them to OpenGL, then call Triangles, which should connect the points for me.. right?
EDIT:
A picture might be nice!
your z must be calculated using phi. float z = inLength * (float)Math.cos(inPhi);
Also,the points generated are not triangles so it would be better to use GL_LINE_STRIP
Using triangle strip on Polar sphere is as easy as drawing points in pairs, for example:
const float GL_PI = 3.141592f;
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles
GLfloat radius = 60.0f;
const int gradation = 20;
for (alpha = 0.0; alpha < GL_PI; alpha += GL_PI/gradation)
{
glBegin(GL_TRIANGLE_STRIP);
for (beta = 0.0; beta < 2.01*GL_PI; beta += GL_PI/gradation)
{
x = radius*cos(beta)*sin(alpha);
y = radius*sin(beta)*sin(alpha);
z = radius*cos(alpha);
glVertex3f(x, y, z);
x = radius*cos(beta)*sin(alpha + GL_PI/gradation);
y = radius*sin(beta)*sin(alpha + GL_PI/gradation);
z = radius*cos(alpha + GL_PI/gradation);
glVertex3f(x, y, z);
}
glEnd();
}
First point entered is as follows the formula, and the second one is shifted by the single step of alpha angle (from the next parallel).
I hope there will be an easy answer, as often times, something stripped out of Compact Framework has a way of being performed in a seemingly roundabout manner, but works just as well as the full framework (or can be made more efficient).
Simply put, I wish to be able to do a function similar to System.Drawing.Graphics.DrawArc(...) in Compact Framework 2.0.
It is for a UserControl's OnPaint override, where an arc is being drawn inside an ellipse I already filled.
Essentially (close pseudo code, please ignore imperfections in parameters):
FillEllipse(ellipseFillBrush, largeEllipseRegion);
DrawArc(arcPen, innerEllipseRegion, startAngle, endAngle); //not available in CF
I am only drawing arcs in 90 degree spaces, so the bottom right corner of the ellipse's arc, or the top left. If the answer for ANY angle is really roundabout, difficult, or inefficient, while there's an easy solution for just doing just a corner of an ellipse, I'm fine with the latter, though the former would help anyone else who has a similar question.
I use this code, then use FillPolygon or DrawPolygon with the output points:
private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int Radius, int xOffset, int yOffset, int LineWidth)
{
if(PointsInArc < 0)
PointsInArc = 0;
if(PointsInArc > 360)
PointsInArc = 360;
Point[] points = new Point[PointsInArc * 2];
int xo;
int yo;
int xi;
int yi;
float degs;
double rads;
for(int p = 0 ; p < PointsInArc ; p++)
{
degs = StartAngle + ((SweepAngle / PointsInArc) * p);
rads = (degs * (Math.PI / 180));
xo = (int)(Radius * Math.Sin(rads));
yo = (int)(Radius * Math.Cos(rads));
xi = (int)((Radius - LineWidth) * Math.Sin(rads));
yi = (int)((Radius - LineWidth) * Math.Cos(rads));
xo += (Radius + xOffset);
yo = Radius - yo + yOffset;
xi += (Radius + xOffset);
yi = Radius - yi + yOffset;
points[p] = new Point(xo, yo);
points[(PointsInArc * 2) - (p + 1)] = new Point(xi, yi);
}
return points;
}
I had this exactly this problem and me and my team solved that creating a extension method for compact framework graphics class;
I hope I could help someone, cuz I spent a lot of work to get this nice solution
Mauricio de Sousa Coelho
Embedded Software Engineer
public static class GraphicsExtension
{
// Implements the native Graphics.DrawArc as an extension
public static void DrawArc(this Graphics g, Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle)
{
//Configures the number of degrees for each line in the arc
int degreesForNewLine = 5;
//Calculates the number of points in the arc based on the degrees for new line configuration
int pointsInArc = Convert.ToInt32(Math.Ceiling(sweepAngle / degreesForNewLine)) + 1;
//Minimum points for an arc is 3
pointsInArc = pointsInArc < 3 ? 3 : pointsInArc;
float centerX = (x + width) / 2;
float centerY = (y + height) / 2;
Point previousPoint = GetEllipsePoint(x, y, width, height, startAngle);
//Floating point precision error occurs here
double angleStep = sweepAngle / pointsInArc;
Point nextPoint;
for (int i = 1; i < pointsInArc; i++)
{
//Increments angle and gets the ellipsis associated to the incremented angle
nextPoint = GetEllipsePoint(x, y, width, height, (float)(startAngle + angleStep * i));
//Connects the two points with a straight line
g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);
previousPoint = nextPoint;
}
//Garantees connection with the last point so that acumulated errors cannot
//cause discontinuities on the drawing
nextPoint = GetEllipsePoint(x, y, width, height, startAngle + sweepAngle);
g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);
}
// Retrieves a point of an ellipse with equation:
private static Point GetEllipsePoint(float x, float y, float width, float height, float angle)
{
return new Point(Convert.ToInt32(((Math.Cos(ToRadians(angle)) * width + 2 * x + width) / 2)), Convert.ToInt32(((Math.Sin(ToRadians(angle)) * height + 2 * y + height) / 2)));
}
// Converts an angle in degrees to the same angle in radians.
private static float ToRadians(float angleInDegrees)
{
return (float)(angleInDegrees * Math.PI / 180);
}
}
Following up from #ctacke's response, which created an arc-shaped polygon for a circle (height == width), I edited it further and created a function for creating a Point array for a curved line, as opposed to a polygon, and for any ellipse.
Note: StartAngle here is NOON position, 90 degrees is the 3 o'clock position, so StartAngle=0 and SweepAngle=90 makes an arc from noon to 3 o'clock position.
The original DrawArc method has the 3 o'clock as 0 degrees, and 90 degrees is the 6 o'clock position. Just a note in replacing DrawArc with CreateArc followed by DrawLines with the resulting Point[] array.
I'd play with this further to change that, but why break something that's working?
private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int ellipseWidth, int ellipseHeight, int xOffset, int yOffset)
{
if (PointsInArc < 0)
PointsInArc = 0;
if (PointsInArc > 360)
PointsInArc = 360;
Point[] points = new Point[PointsInArc];
int xo;
int yo;
float degs;
double rads;
//could have WidthRadius and HeightRadius be parameters, but easier
// for maintenance to have the diameters sent in instead, matching closer
// to DrawEllipse and similar methods
double radiusW = (double)ellipseWidth / 2.0;
double radiusH = (double)ellipseHeight / 2.0;
for (int p = 0; p < PointsInArc; p++)
{
degs = StartAngle + ((SweepAngle / PointsInArc) * p);
rads = (degs * (Math.PI / 180));
xo = (int)Math.Round(radiusW * Math.Sin(rads), 0);
yo = (int)Math.Round(radiusH * Math.Cos(rads), 0);
xo += (int)Math.Round(radiusW, 0) + xOffset;
yo = (int)Math.Round(radiusH, 0) - yo + yOffset;
points[p] = new Point(xo, yo);
}
return points;
}