How to paint a line from another method in OpenTk - opentk

I have a program that will draw a line as shown below.
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
GL.glMatrixMode(GL.GL_MODELVIEW);
GL.glLoadIdentity();
GL.glColor(Color.Yellow);
GL.glBegin(GL.GL_LINES);
GL.glVertex3f(100.0f, 100.0f, 0.0f); // origin of the line
GL.glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the line
GL.glEnd();
glControl1.SwapBuffers();
}
The method above is called during Paint event.
But I have another method as shown below:
private void glControl1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
GL.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
GL.glMatrixMode(GL.GL_MODELVIEW);
GL.glLoadIdentity();
GL.glColor(Color.Yellow);
GL.glBegin(GL.GL_LINES);
GL.glVertex3f(100.0f, 100.0f, 0.0f); // origin of the FIRST line
GL.glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the FIRST line
GL.glVertex3f(120.0f, 170.0f, 10.0f); // origin of the SECOND line
GL.glVertex3f(240.0f, 120.0f, 5.0f); // ending point of the SECOND line
GL.glEnd();
}
I wish to draw something in this method but it didn't work.
What's wrong.
Thanks

You should call glControl1.SwapBuffers(); after all your drawing at the end of your Paint event.
SwapBuffers will present the current buffer to the screen. usually you have two of those buffers switching all the time in render loops. You can clear it calling
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
What your second method does, is painting outside the Paint loop. You either need to use SwapBuffers in that event or queue your drawing and work the queue in your paint event.
Depending on how complex your drawing code gets, it may be appropriate to introduce a concept of a "scene" holding all objects that are to be drawn on each paint invocation.

Related

VariableSizeWrapGrid: remove item when started to drag it

I have a VariableSizeWrapGrid and custom drag/drop handling logic. I need to remove (or collapse) the item when it began being dragged. VariableSizeWrapGrid's dataSource is ObservableCollection.
Collapsing (Visibility=Collapsed) an item does not work: it becomes invisible, but occupies its original space anyway. Invalidating VariableSizeWrapGrid does not get rid of these spaces.
Removing an item does not work because dragging process is aborted by some unknown reasons when i'm removing dragged item from its original source. Removing any other items does not abort dragging. I've overridden void OnItemsChanged(object e) with empty handler (not calling base version), but this does not help too.
Short code sample:
void VariableGridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
var tile = e.Items[0] as Tile;
tile.removeFromOwnerContainer(); // this line interrupts dragging
}

How to change the value of SurfaceSlider without calling the ValueChanged event?

Im working with a MediaElement object and SurfaceSlider object, I use the SurfaceSlider to control the Video position and also want the SurfaceSlider to show the current position of the video, like youtube does.
I use this code to control the position of the video, this function is called when the ValueChanged event of the SurfaceSlider object occurs...
private void SeekToMediaPosition(object sender, RoutedPropertyChangedEventArgs<double> args)
{
int SliderValue = (int)mySurfaceSlider.Value;
TimeSpan ts = new TimeSpan(0, 0, 0, 0, SliderValue);
myMediaElement.Position = ts;
}
I use this code to show the current position of the video...
DispatcherTimer ticks = new DispatcherTimer();
private void Element_MediaOpened(object sender, EventArgs e)
{
mySurfaceSlider.Maximum = myMediaElement.NaturalDuration.TimeSpan.TotalMilliseconds;
ticks.Interval = TimeSpan.FromMilliseconds(1);
ticks.Tick += ticks_Tick;
ticks.Start();
myMediaElement.Play();
}
void ticks_Tick(object sender, object e)
{
mySurfaceSlider.Value = myMediaElement.Position.TotalMilliseconds;
}
The problem is when the SurfaceSlider value is changed showing the current position of the video, the ValueChanged event is called too, and the position of the video is changed, creating a loop I guess.
Is there any other event to be used when the user changes the SurfaceSlider value, or a way to handle this issue?
Thanks
Esteban,
I think there are a few things I would try.
1... Increase your TimeSpan.FromMilliseconds(1) to TimeSpan.FromMilliseconds(100) or higher. I don't think your slider needs to be polling your video so continuously.
2... I think you could set a flag in SeekToMediaPosition (SeekInProgress=true) before you set myMediaElement.Position. Then in ticks_Tick, if SeekInProgress==true, set SeekInProgress=false and don't set mySurfaceSlider.Value for one timer cycle.
I believe the original version of the code you are using comes from here: http://msdn.microsoft.com/en-us/library/ms748248.aspx ... Perhaps there is something that code was doing to prevent your problem that has been omitted from your implementation?
If the slider is jumping around, maybe there is lag between when the user places their finger down on the slider and when they move it (causing SeekToMediaPosition to fire). What you need to do is detect if the user is currently touching mySurfaceSlider in ticks_Tick (maybe there is a collection of touch points to query for intersection with mySurfaceSlider? Or does mySurfaceSlider have touchdown and touchup events?) In any case, if the user is even TOUCHING mySurfaceSlider, you shouldn't let ticks_Tick update mySurfaceSlider.Value (or for a few milliseconds after).

Drawing control with transparent background

I've been trying to display an image which has a transparent border as the background to a control.
Unfortunately, the transparent area creates a hole in the parent form as follows:
In the above image, the form has a red background which I'd hoped to see behind my control in the transparent areas.
The code I used is as follows:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if (this.Image != null)
{
Graphics g = Graphics.FromImage(this.Image);
ImageAttributes attr = new ImageAttributes();
//set the transparency based on the top left pixel
attr.SetColorKey((this.Image as Bitmap).GetPixel(0, 0), (this.Image as Bitmap).GetPixel(0, 0));
//draw the image using the image attributes.
Rectangle dstRect = new Rectangle(0, 0, this.Image.Width, this.Image.Height);
e.Graphics.DrawImage(this.Image, dstRect, 0, 0, this.Image.Width, this.Image.Height,
GraphicsUnit.Pixel, attr);
}
else
{
base.OnPaint(e);
}
}
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
//base.OnPaintBackground(e);
}
This class is inherited from a PictureBox because I needed a control which implements OnMouseMove and OnMouseUp Events.
I've been researching most of the day without success testing out different ideas but unfortunately most only work on the full framework and not .Net CF.
Any ideas would be much appreciated.
Ah the joys of CF transparency. I could go on and on about it (and have in my blog and the Project Resistance code I did ages ago).
The gist is this. The child control has to paint it's areas, but first it has to call back up to it's parent (the Form in your case) and tell it to redraw it's background image everywhere except in the child's clipping region and then draw itself on top of that. If that sounds a bit confusing it's because it is.
For example, if you look at Project Resistance, a View (which is just a Control) draws a resistor and bands. It lies in a Form that has an image background, and that background needs to "show through" the transparent areas of the resistor:
So in the drawing code of the resistor it does this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
try
{
RECT rect = new RECT(this.Bounds);
// draw the blank
Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_blankImage, Bounds,
new Rectangle(0, 0, m_blankImage.Width, m_blankImage.Height));
if (m_bandsImage != null)
{
// draw the bands
Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_bandsImage, Bounds,
new Rectangle(0, 0, m_bandsImage.Width, m_bandsImage.Height));
}
}
finally
{
}
if (!Controller.TouchMode)
{
// TODO: draw in the selection arrow
// Controller.SelectedBand
}
}
Which is simple enough. The key is that it calls to it's base OnPaint, which does this:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// this assumes we're in a workspace, on MainForm (the whole Parent.Parent thing)
IBackgroundPaintProvider bgPaintProvider = Parent.Parent as IBackgroundPaintProvider;
if (bgPaintProvider != null)
{
Rectangle rcPaint = e.ClipRectangle;
// use the parent, since it's the workspace position in the Form we want,
// not our position in the workspace
rcPaint.Offset(Parent.Left, Parent.Top);
bgPaintProvider.PaintBackground(e.Graphics, e.ClipRectangle, rcPaint);
}
}
You can see it's calling PaintBackground of the containing Form (it's Parent.Parent in this case becuse the Control is actually in a container called a Workspace - you wouldn't need to walk up twice in your case). That draws in the background image in the area you're currently seeing as the "hole"
public void PaintBackground(Graphics g, Rectangle targetRect, Rectangle sourceRect)
{
g.DrawImage(m_bmBuffer, targetRect, sourceRect, GraphicsUnit.Pixel);
}

(C++/CLI) Testing Mouse Position in a Rectangle Relevent to Parent

I've been messing around with the Graphics class to draw some things on a panel. So far to draw, I've just been using the Rectangle Structure. On a panel, by clicking a button, it makes a rectangle in a random place and adds it to an array of other rectangles (They're actually a class called UIElement, which contains a Rectangle member). When this panel is clicked, it runs a test with all the elements to see if the mouse is inside any of them, like this:
void GUIDisplay::checkCollision()
{
Point cursorLoc = Cursor::Position;
for(int a = 0; a < MAX_CONTROLS; a++)
{
if(elementList[a] != nullptr)
{
if(elementList[a]->bounds.Contains(cursorLoc))
{
elementList[a]->Select();
//MessageBox::Show("Click!", "Event");
continue;
}
elementList[a]->Deselect();
}
}
m_pDisplay->Refresh();
}
The problem is, when I click the rectangle, nothing happens.
The UIElement class draws its rectangles in the following bit of code. However, I've modified it a bit, because in this example it uses the DrawReversibleFrame method to do the actually drawing, as I was using Graphics.FillRectangle method. When I changed it, I noticed DrawReversibleFrame drew in a different place than FillRectangle. I believe this is because DrawReversibleFrame draws with its positions relative to the window, while FillRectangle does it relative to whatever Paint event its in (Mines in a panel's Paint method.) So let me just show the code:
void UIElement::render(Graphics^ g)
{
if(selected)
{
Pen^ line = gcnew Pen(Color::Black, 3);
//g->FillRectangle(gcnew SolidBrush(Color::Red), bounds);
ControlPaint::DrawReversibleFrame(bounds, SystemColors::Highlight, FrameStyle::Thick);
g->FillRectangle(gcnew SolidBrush(Color::Black), bounds);
//g->DrawLine(line, bounds.X, bounds.Y, bounds.Size.Width, bounds.Size.Height);
}
else
{
ControlPaint::DrawReversibleFrame(bounds, SystemColors::ControlDarkDark, FrameStyle::Thick);
//g->FillRectangle(gcnew SolidBrush(SystemColors::ControlDarkDark), bounds);
}
}
I add in both DrawReverisbleFrame and FillRectangle so that way I could see the difference. This is what it looked like when I clicked the frame drawn by DrawReversibleFrame:
The orange frame is where I clicked, the black is where its rendering. This shows me that the Rectangle's Contains() method is look for the rectangle relevant to the window, and not the panel. That's what I need fixed :)
I'm wondering if this is happening because the collision is tested outside of the panels Paint method. But I don't see how I could implement this collision testing inside the Paint method.
UPDATE:
Ok, so I just discovered that it appears that what DrawReversibleFrame and FillRectangle draw are always a certain distance apart. I don't quite understand this, but someone else might.
Both Cursor::Position and DrawReversableFrame operate in screen coordinates. That is for the entire screen, everything on your monitor, and not just your window. FillRectangle on the other hand operates on window coordinates, that is the position within your window.
If you take your example where you were drawing with both and the two boxes are always the same distance apart, and move your window on the screen then click again, you will see that the difference between the two boxes changes. It will be the difference between the top left corner of your window and the top left corner of the screen.
This is also why when you check to see what rectangle you clicked isn't hitting anything. You are testing the cursor position in screen coordinates against the rectangle coordinates in window space. It is possible that it would hit one of the rectangles, but it probably won't be the one you actually clicked on.
You have to always know what coordiante systems your variables are in. This is related to the original intention of Hungarian Notation which Joel Spolsky talks about in his entry Making Wrong Code Look Wrong.
Update:
PointToScreen and PointToClient should be used to convert coordinates between screen and window coordinates.

Android View.onDraw() always has a clean Canvas

I am trying to draw an animation. To do so I have extended View and overridden the onDraw() method. What I would expect is that each time onDraw() is called the canvas would be in the state that I left it in and I could choose to clear it or just draw over parts of it (This is how it worked when I used a SurfaceView) but each time the canvas comes back already cleared. Is there a way that I can not have it cleared? Or maybe save the previous state into a Bitmap so I can just draw that Bitmap and then draw over top of it?
I'm not sure if there is a way or not. But for my custom views I either redraw everything each time onDraw() is called, or draw to a bitmap and then draw the bitmap to the canvas (like you suggested in your question).
Here is how i do it
class A extends View {
private Canvas canvas;
private Bitmap bitmap;
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (bitmap != null) {
bitmap .recycle();
}
canvas= new Canvas();
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
}
public void destroy() {
if (bitmap != null) {
bitmap.recycle();
}
}
public void onDraw(Canvas c) {
//draw onto the canvas if needed (maybe only the parts of animation that changed)
canvas.drawRect(0,0,10,10,paint);
//draw the bitmap to the real canvas c
c.drawBitmap(bitmap,
new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()),
new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()), null);
}
}
you should have a look here to see the difference between basic view and surfaceView. A surfaceView has a dedicated layer for drawing, which I suppose keeps track of what you drew before. Now if you really want to do it on a basic View, you could try to put each item you draw in an array, like the exemple of itemized overlay for the mapview.
It should work pretty much the same way
Your expectations do not jib w/ reality :) The canvas will not be the way you left it, but it blank instead. You could create an ArrayList of objects to be drawn (canvas.drawCircle(), canvas.drawBitmap() etc.), then iterate though the ArrayList in the OnDraw(). I am new to graphics programming but I have used this on a small scale. Maybe there is a much better way.