I have a weird problem concerning the Region object.
I created a method which returns the Region defined by an array of Point3D (which is just a 3-float struct) which are converted to PointF.(Face is the face of a 3d cube)
public Region GetRegion(params Point3D[] facePoints)
{
float distance = Camera3DLib.Camera3D.GetDistance();
PointF[] planePoints = new PointF[links.Length];
for (int i = 0; i < planePoints.Length; i++)
{
planePoints[i] = facePoints[i].ToPointF(distance);
}
GraphicsPath pth = new GraphicsPath();
pth.AddPolygon(planePoints);
return new Region(pth);
}
The problem is displaying this region.
When I use FillRegion, the graphics fills everything except the region.
To get the region needed to fill, I use the above method a number of times (Solid3D contains an array of Face3D and an array of vertices, I hope the purpose of the methods that I use are self explanatory by their names)
private static Region GetVisibleRegion(Solid3D s)
{
Region visible = new Region();
for (int i = 0; i < s.GetFaces().Length; i++)
{
Face3D face = s.GetFaces()[i];
Point3D[] facePoints = s.GetFaceVertices(i);
Region region = face.GetRegion(facePoints);
visible.Union(region);
}
return visible;
}
The drawing itself is done in a method called from the Form_paint event:
gr.FillRegion(Brushes.Blue, GetVisibleRegion(arr[0].Key));
gr is the graphics and arr is a dictionary who's key is a Solid3D.
This causes everything except the faces to be added to the Region.
What would be the cause of this?
I thought maybe the object, being passed by pointers between methods, is somehow getting reset (to default values of members) but I'm not so sure that's it.
PS: The code I wrote doesn't throw exceptions, so (I think) it's a logic or usage problem. If there are any questions about parts of the implementation that which aren't posted here (it's a lot of code :P) please ask.
Thanks!
Related
It seems that WPF's InkCanvas is only able to provide the points of the stroke (independent of the width and height of the stroke). For an application, I need to know all the points that are drawn by the InkCanvas.
For instance, assume that the width and height of the stroke are 16. Using this stroke size I paint a dot on the InkCanvas. Is there a straightforward way to obtain all 256 pixels in this dot (and not the center point of this giant dot alone)?
Why I care:
In my application, the user uses an InkCanvas to draw on top of a Viewport3D which is displaying a few 3D objects. I want to use all the points of the strokes to perform ray casting and determine which objects in the Viewport3D have been overlaid by the user's strokes.
I found a very dirty way of handling this. If anyone knows of a better method, I'll be more than happy to upvote and accept their response as an answer.
Basically my method involves getting the Geometry of each stroke, traversing all the points inside the boundaries of that geometry and determining whether the point is inside the geometry or not.
Here's the code that I am using now:
foreach (var stroke in inkCanvas.Strokes)
{
List<Point> pointsInside = new List<Point>();
Geometry sketchGeo = stroke.GetGeometry();
Rect strokeBounds = sketchGeo.Bounds;
for (int x = (int)strokeBounds.TopLeft.X; x < (int)strokeBounds.TopRight.X + 1; x++)
for (int y = (int)strokeBounds.TopLeft.Y; y < (int)strokeBounds.BottomLeft.Y + 1; y++)
{
Point p = new Point(x, y);
if (sketchGeo.FillContains(p))
pointsInside.Add(p);
}
}
You can use the StrokeCollection's HitTest method. I've compared the performance of your solution with this implementation and found the HitTest method performs better. Your mileage etc.
// get our position on our parent.
var ul = TranslatePoint(new Point(0, 0), this.Parent as UIElement);
// get our area rect
var controlArea = new Rect(ul, new Point(ul.X + ActualWidth, ul.Y + ActualHeight));
// hit test for any strokes that have at least 5% of their length in our area
var strokes = _formInkCanvas.Strokes.HitTest(controlArea, 5);
if (strokes.Any())
{
// do something with this new knowledge
}
You can find the documentation here:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.ink.strokecollection.hittest?view=netframework-4.7.2
Further, if you only care if any point is in your rect, you can use the code below. It's an order of magnitude faster than StrokeCollection.HitTest because it doesn't care about percentages of strokes so it does a lot less work.
private bool StrokeHitTest(Rect bounds, StrokeCollection strokes)
{
for (int ix = 0; ix < strokes.Count; ix++)
{
var stroke = strokes[ix];
var stylusPoints = stroke.DrawingAttributes.FitToCurve ?
stroke.GetBezierStylusPoints() :
stroke.StylusPoints;
for (int i = 0; i < stylusPoints.Count; i++)
{
if (bounds.Contains((Point)stylusPoints[i]))
{
return true;
}
}
}
return false;
}
How is the approach to plot complex drawings with Direct2D (Sharpdx)?
Actually I am using a WindowsRenderTarget, connecting it with a Direct2D1.Factory and drawing to a RenderControl.
Factory2D = new SharpDX.Direct2D1.Factory(FactoryType.MultiThreaded);
FactoryWrite = new SharpDX.DirectWrite.Factory();
var properties = new HwndRenderTargetProperties();
properties.Hwnd = this.Handle;
properties.PixelSize = new Size2(this.ClientSize.Width, this.ClientSize.Height);
properties.PresentOptions = PresentOptions.RetainContents;
RenderTarget2D = new WindowRenderTarget(Factory2D, new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)), properties);
RenderTarget2D.AntialiasMode = AntialiasMode.PerPrimitive;
The drawing is done in the Paint Event of the form:
RenderTarget2D.BeginDraw();
RenderTarget2D.Clear(Color4.Black);
drawProgress(); // Doing Paintings like DrawLine, Multiple PathGeometrys, DrawEllipse and DrawText
RenderTarget2d.EndDraw();
In the MouseMove/MouseWheel event the drawing will be recalculated (for scaling or calculation of the elements that will be displayed). This process need about 8-10ms.
The next step is actually
this.Refresh();
Here, I guess, is the problem, this progress needs up to 140ms.
So the scaling/moving of the plot has about 7fps.
Also the program occupies more and more memory when refreshing the Control
////Edit
Painting of lines:
private void drawLines(Pen pen, PointF[] drawElements)
{
SolidColorBrush tempBrush = new SolidColorBrush(RenderTarget2D, SharpDX.Color.FromRgba(pen.Color.ToArgb()));
int countDrawing = (drawElements.Length / 2) + drawElements.Length % 2;
for (int i = 0; i < countDrawing; i++)
{
drawLine(new Vector2(drawElements[i].X, drawElements[i].Y), new Vector2(drawElements[i + 1].X, drawElements[i + 1].Y), brushWhite);
}
}
Painting geometrys:
RenderTarget2D.DrawGeometry(graphicPathToPathGeometry(p), penToSolidColorBrush(pen));
private PathGeometry graphicPathToPathGeometry(GraphicsPath path)
{
geometry = new PathGeometry(Factory2D);
sink = geometry.Open();
if (path.PointCount > 0)
{
sink.BeginFigure(new Vector2(path.PathPoints[path.PointCount - 1].X, path.PathPoints[path.PointCount - 1].Y), FigureBegin.Hollow);
sink.AddLines(pointFToVector2(path.PathPoints));
sink.EndFigure(new FigureEnd());
sink.Close();
}
return geometry;
}
In mouse move the drawing will be recalculated by just building differences between Cursor.Position.X/Y old and Cursor.Position.X/Y new. So the the lines will be recalculated really often :)
The main bottleneck is your graphicPathToPathGeometry() function. You are creating and "filling" a PathGeometry in a render loop. As I mentioned above, a core principle is that you have to create your resources at once and then just reuse them in your drawing routine(s).
About your memory leak... your code samples don't provide enough information, but most probably you are not freeing the resources that you are creating (ie PathGeometry, SolidColorBrush and the ones we don't see).
The simplest advise is - use your render loop only for rendering/drawing and reuse resources instead of recreating them.
Improving the performance of Direct2D apps
One part of the problem is:
SolidColorBrush tempBrush = new SolidColorBrush(RenderTarget2D, SharpDX.Color.FromRgba(pen.Color.ToArgb()));
Creating objects of any kind inside the renderloop creates a big memory lack in the application. Drawing existing values is the way to go.
I guess the performance issue will also be based on this problem.
I'm trying to build an application that solves a puzzle (trying to develop a graph algorithm), and I don't want to enter sample input by hand all the time.
Edit: I'm not trying to build a game. I'm trying to build an agent which plays the game "SpellSeeker"
Say I have an image (see attachment) on the screen with numbers in it, and I know the locations of the boxes, and I have the exact images for these numbers. What I want to do is simply tell which image (number) is on the corresponding box.
So I guess I need to implement
bool isImageInsideImage(Bitmap numberImage,Bitmap Portion_Of_ScreenCap) or something like that.
What I've tried is (using AForge libraries)
public static bool Contains(this Bitmap template, Bitmap bmp)
{
const Int32 divisor = 4;
const Int32 epsilon = 10;
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(0.9f);
TemplateMatch[] tm = etm.ProcessImage(
new ResizeNearestNeighbor(template.Width / divisor, template.Height / divisor).Apply(template),
new ResizeNearestNeighbor(bmp.Width / divisor, bmp.Height / divisor).Apply(bmp)
);
if (tm.Length == 1)
{
Rectangle tempRect = tm[0].Rectangle;
if (Math.Abs(bmp.Width / divisor - tempRect.Width) < epsilon
&&
Math.Abs(bmp.Height / divisor - tempRect.Height) < epsilon)
{
return true;
}
}
return false;
}
But it returns false when searching for a black dot in this image.
How can I implement this?
I'm answering my question since I've found the solution:
this worked out for me:
System.Drawing.Bitmap sourceImage = (Bitmap)Bitmap.FromFile(#"C:\SavedBMPs\1.jpg");
System.Drawing.Bitmap template = (Bitmap)Bitmap.FromFile(#"C:\SavedBMPs\2.jpg");
// create template matching algorithm's instance
// (set similarity threshold to 92.5%)
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f);
// find all matchings with specified above similarity
TemplateMatch[] matchings = tm.ProcessImage(sourceImage, template);
// highlight found matchings
BitmapData data = sourceImage.LockBits(
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
ImageLockMode.ReadWrite, sourceImage.PixelFormat);
foreach (TemplateMatch m in matchings)
{
Drawing.Rectangle(data, m.Rectangle, Color.White);
MessageBox.Show(m.Rectangle.Location.ToString());
// do something else with matching
}
sourceImage.UnlockBits(data);
The only problem was it was finding all (58) boxes for said game. But changing the value 0.921f to 0.98 made it perfect, i.e. it finds only the specified number's image (template)
Edit: I actually have to enter different similarity thresholds for different pictures. I found the optimized values by trying, in the end I have a function like
float getSimilarityThreshold(int number)
A better approach is to build a custom class which holds all the information you need instead of relying on the image itself.
For example:
public class MyTile
{
public Bitmap TileBitmap;
public Location CurrentPosition;
public int Value;
}
This way you can "move around" the tile class and read the value from the Value field instead of analyzing the image. You just draw whatever image the class hold to the position it's currently holding.
You tiles can be held in an array like:
private list<MyTile> MyTiles = new list<MyTile>();
Extend class as needed (and remember to Dispose those images when they are no longer needed).
if you really want to see if there is an image inside the image, you can check out this extension I wrote for another post (although in VB code):
Vb.Net Check If Image Existing In Another Image
First time I ever ask a question here so correct me if I´m doing it wrong.
Picture of my chess set:
Every time I move a piece it lags for about 1 second. Every piece and tile has an Image and there is exactly 96 Images. Every time I move a piece it clears everything with black and then update the graphics.
In the early stages of the chess I didn't have any Images and used different colors instead and only a few pieces there was no noticeable lag and the piece moved in an instant.
public void updateGraphics(PaintEventArgs e, Graphics g, Bitmap frame)
{
g = Graphics.FromImage(frame);
g.Clear(Color.Black);
colorMap(g);
g.Dispose();
e.Graphics.DrawImageUnscaled(frame, 0, 0);
}
The function colorMap(g) looks like this:
private void colorMap(Graphics g)
{
for (int y = 0; y < SomeInts.amount; y++)
{
for (int x = 0; x < SomeInts.amount; x++)
{
//Tiles
Bundle.tile[x, y].colorBody(g, x, y);
//Pieces
player1.colorAll(g);
player2.colorAll(g);
}
}
}
The colorAll function executes every pieces colorBody(g) function which look like this:
public void colorBody(Graphics g)
{
//base.colorBody() does the following: body = new Rectangle(x * SomeInts.size + SomeInts.size / 4, y * SomeInts.size + SomeInts.size / 4, size, size);
base.colorBody();
if (team == 1)
{
//If its a white queen
image = Image.FromFile("textures/piece/white/queen.png");
}
if (team == 2)
{
//If its a black queen
image = Image.FromFile("textures/piece/black/queen.png");
}
g.DrawImage(image, body);
}
and finaly the function that moves the piece:
public void movePiece(MouseEventArgs e)
{
for (int y = 0; y < SomeInts.amount; y++)
{
for (int x = 0; x < SomeInts.amount; x++)
{
if (Bundle.tile[x, y].body.Contains(e.Location))
{
//Ignore this
for (int i = 0; i < queens.Count; i++)
{
Queen temp = queens.ElementAt<Queen>(i);
temp.move(x, y);
}
//Relevant
player1.move(x, y);
player2.move(x, y);
}
}
}
}
Thank you for reading all this! I could make a link to the whole program if my coding examples is not enough.
You're calling Image.FromFile in every refresh, for every image - effectively reloading every image file every time from disk.
Have you considered loading the images once, and storing the resulting Images somewhere useful? (say, an array, Image[2,6] would be adequate)
Why do you redraw the board each time? Can't you just leave the board where it is and display an image with transparent background over it? That way you have one image as a background (the board), plus 64 smaller images placed over the board in a grid and just change the image being displayed on each move.
That way, you can let windows handle the drawing...
Also, load the images of the pieces at the start of the application.
In addition to not calling Image.FromFile() inside updateGraphics() (which is definitely your biggest issue), you shouldn't be attempting to redraw the entire board every on every call to updateGraphics() - most of the time, only a small portion of the board will be invalidated.
The PaintEventArgs contains an parameter, ClipRectangle, which specifies which portion of the board needs redrawing. See if you can't figure out which tiles intersect with that rectangle, and only redraw those tiles :)
Hint: Write a function Point ScreenToTileCoords(Point) which takes a screen coordinate and returns which board-tile is at that coordinate. Then the only tiles you need to redraw are
Point upperLeftTileToBeDrawn = ScreenToTileCoords(e.ClipRectangle.Left, e.ClipRectangle.Top);
Point lowerRightTileToBeDrawn = ScreenToTileCoords(e.ClipRectangle.Right - 1, e.ClipRectangle.Bottom- 1);
Also, make sure your control is double-buffered, to avoid tearing. This is much simpler than #Steve B's link in the comments above states; assuming this is a UserControl, simply set
this.DoubleBuffered = true;
Well, what about this:
Do not clear the whole board but only those parts that need to be cleared.
Alternative:
Update to WPF - it moves drawing to the graphics card - and just move pieces around, in a smart way (i.e. have a control / object for every piece).
I have a problem in the game I wrote with XNA. I recently added Textured polygons, and saw that every textured polygon shared the same texture although I changed it before calling. The code I am using:
if (countMeshes > 0)
{
for (int i = 0; i < countMeshes; i++)
{
TexturedMesh curMesh = listMeshes[i];
if (curMesh.tex == null)
{
drawEffect.Texture = WHITE_TEXTURE;
}
else
{
drawEffect.Texture = curMesh.tex;
}
drawEffect.Techniques[0].Passes[0].Apply();
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, curMesh.verts, 0, curMesh.count);
}
}
Now, the first that came into my mind would be to create a BasicEffect for each Texture I need to draw. But I think that would be a bit of a overkill so my question is: How should I do it?
PS: I double checked everything, the UV Coords are fine, and it is 2D.
It seems to be the only way, the way I did it was to create a Dictionary with the texture as the key and a struct of a basic effect and a list of Vertexs as the value.
Something like this:
public struct MeshEffect
{
public TexturedMesh mesh;
public BasicEffect effect;
}
and the Dictionary:
private Dictionary<Texture2D, MeshEffect> texturedMeshes = new Dictionary<Texture2D, MeshEffect>();
But it all really depends on how you handle drawing, but one thing is sure, you can't use more than one texture on one BasicEffect.