VisuaBrush Scaling Incorrectly with Canvas as Visual element - c#

I've been fighting with this problem for a few days now and have had zero luck getting it resolved or finding any support on the web. Basically, I am trying to create a VisualBrush with a canvas as the visual element. I want to be able to draw several lines on this canvas and will eventually be mapping it to a 3D surface (2 parallel triangles forming a rectangle). I've set up the texture coordinates and can confirm everything works by simply using an ImageBrush or something of that nature. The problem I am having is that the canvas refuses to maintain its size and is constantly scaled based on the content that is inside it. So for example, if the canvas is 100x100 and I have a single line inside it from (0, 0) to (50, 50), the canvas would be scaled such that only the 50x50 portion with content inside is mapped to the texture coordinates and onto the 3D surface. I have tried many combinations of Viewport/Viewbox/StretchMode/Alignment/etc and can't find anything that stops this from happening. I am setting the Width and Height properties of the canvas so I can't see why it would perform differently in the VisualBrush versus if I simply added it into a grid or some other layout container.
Here's some code to help illustrate the problem.
// create the canvas for the VisualBrush
Canvas drawCanvas = new Canvas();
drawCanvas.Background = Brushes.Transparent; // transparent canvas background so only content is rendered
drawCanvas.Width = 100;
drawCanvas.Height = 100;
// add a line to the canvas
Line l = new Line();
l.X1 = 0;
l.Y1 = 0;
l.X2 = 50;
l.Y2 = 50;
l.Stroke = Brushes.Red;
l.StrokeThickness = 2;
drawCanvas.Children.Add(l);
// create rectangular surface mesh
MeshGeometry3D TableMesh = new MeshGeometry3D();
CreateRectangleModel(
new Point3D(0, 0, 0),
new Point3D(0, 0, 100),
new Point3D(100, 0, 100),
TableMesh);
// need to include texture coordinates for our table mesh to map canvas to 3D model
TableMesh.TextureCoordinates = new PointCollection {
new Point(0, 0), new Point(0, 1), new Point(1, 1),
new Point(1, 1), new Point(0, 1), new Point(0, 0),
new Point(1, 1), new Point(1, 0), new Point(0, 0),
new Point(0, 0), new Point(1, 0), new Point(1, 1)
};
// finally make our visual brush with the canvas we have created
VisualBrush vb = new VisualBrush();
vb.Visual = drawCanvas;
Material TableMaterial = new DiffuseMaterial(vb);
// add our new model to the scene
GeometryModel3D geometry = new GeometryModel3D(TableMesh, TableMaterial);
Model3DGroup group = new Model3DGroup();
group.Children.Add(geometry);
ModelVisual3D previewTable = new ModelVisual3D();
previewTable.Content = group;
MainViewport.Children.Add(previewTable);
And the one function I referenced is
private void CreateRectangleModel(Point3D pStart, Point3D pCorner1, Point3D pEnd, MeshGeometry3D mesh)
{
// pCorner -> O--O <- pEnd
// | /|
// |/ |
// pStart -> O--O <- pCorner2
// find the remaining point for our rectangle
Point3D pCorner2 = new Point3D(
pStart.X + (pEnd.X - pCorner1.X),
pStart.Y + (pEnd.Y - pCorner1.Y),
pStart.Z + (pEnd.Z - pCorner1.Z));
// add necessary triangles to our group (we create 2 facing up, 2 facing down)
CreateTriangleModel(pStart, pCorner1, pEnd, mesh);
CreateTriangleModel(pEnd, pCorner1, pStart, mesh);
CreateTriangleModel(pEnd, pCorner2, pStart, mesh);
CreateTriangleModel(pStart, pCorner2, pEnd, mesh);
}
I can't get the canvas to maintain its set size of 100x100 unless the children content goes all the way to these extremes. For my purposes, the line coordinates could be anywhere within the canvas and I need to have it maintain its desired shape regardless of the content.
Any advice would be greatly appreciated!

Some things to try:
Create an image file of 100x100 pixels by hand and then put it into an ImageBrush, and use that brush in your DiffuseMaterial. This is to confirm that your texture coords, etc are being set up properly.
Instead of using a Transparent Background colour on your Canvas, try Green (this is to see if the texture is being cropped based on transparent pixels)
If the cropping is being done of your texture (due to transparency)...then try putting a rectangle on the Canvas at the dimensions you want the texture to be...maybe that will change something.
Or try using RenderTargetBitmap to create an image from your Canvas visual...then use that image within an ImageBrush. (you can then confirm that the image is of your expected area of 100x100).
Finally, instead of using a Canvas+VisualBrush to create your texture try creating the Brush by using a DrawingBrush+DrawingGroup+GeometryDrawing

Related

Programmatically created UIElement 'unlinks' when local variable value is set to a new UIElement

Hopefully the title isn't to vague, I'm not sure how else to word it. What I'm trying to do (As a basic example) is add a rectangle to a canvas programmatically, then at some later point change the local variable to a new rectangle with different properties and have that update on the canvas.
// First rectangle
Rectangle rect = new Rectangle()
{
Width = 50,
Height = 50,
Fill = Brushes.Red,
Margin = new Thickness(20, 20, 0, 0)
};
// Add it to the canvas
mainCanvas.Children.Add(rect);
// Change something about the rectangle, which works
rect.Fill = Brushes.Black;
// Create new rectangle
Rectangle newRect = new Rectangle()
{
Width = 15,
Height = 20,
Fill = Brushes.Blue,
Margin = new Thickness(20, 20, 0, 0)
};
// Set the original rectangle to the new rectangle
rect = newRect;
// Canvas rectangle is no longer 'linked' to the rect variable :(
You are reassigning a the rect variable, but that doesn't affect your Canvas. The Canvas only knows about the old rectangle rect used to point to. rect is just a reference to a rectangle. When you add it to the canvas, the canvas copies the reference. It doesn't continue to use the rect variable anymore. So changing rect to reference a new rectangle changes nothing for the canvas, as the canvas still references the original.
You likely will want to do something like the following. I'm just taking a stab here, so you might need to look up appropriate methods, but hopefully this gives you guidance.
mainCanvas.Children.Remove(rect); //take the old rectangle off the canvas
rect = newRect;
mainCanvas.Children.Add(rect); //replace the new rectangle on the canvas

Create own shape for panel with GraphicsPath

How to set the region like this following picture?
The square I draw was the panel that I want to insert.
Let say the panel size was new Size(200, 500).
Then I want to remove the region of the panel in the middle (with label b and the region was color with blue).
Let say the region was 50,50 in size and the location was 50, 250
how to remove that blue region?
This is what I have done
GraphicsPath a = new GraphicsPath();
Rectangle RectangleRegion = new Rectangle(new point(50, 250), new Size(50,50));
graphicsPath.addRectangle (RectangleRegion)
Panel myPanel = new panel(){new Size(200, 500), new Pont(0,0), region = new region(a);
With this method, it doesn't remove the area I want to have
it just remove the other part I want to keep and it keep the part i want to remove.
Use two Regions and Region.Exclude:
Region r1 = new Region(new Rectangle(11,11,111,111));
Region r2 = new Region(new Rectangle(33,33,66,66));
r1.Exclude(r2);
panel1.Region = r1;
For more complex shapes you will indeed want to create the Regions from GraphicsPaths.

draw multiple curves in WinForms picturebox

I am working on a program which I want to draw diode curves in a WinForms application. I have a list of diode names and I have theire points as you can see at the right side of the picture. That is Voltage as X and Current as Y ( A curve contains like 50 points).
What I want to do is by selecting one or more diodes from the list theire curve show up on my plot. What you see is just a picture box at the moment filled with a bmp. I know that this is not a reliable solution, so I am asking you what can be the best approach to do such thing? I dont know any good component which can make me do this. So I just need to know what can be the best approuch for this task?
A diode curve is something like:
I might have up to 100 of diode curves in my program which all of them (single or multiple) should be drawn by clicking on them in the list.
So what you think?
UPDATE
ALSO important thing is by deselecting a pin in the curve, its curve should be removed from the plot!
I am drawing that axis you see using the code below:
Bitmap xyCords = new Bitmap(500, 500);
Graphics g = Graphics.FromImage(xyCords);
g.DrawLine(penAxis, 250, 0, 250, 500);
g.DrawLine(penAxis, 0, 250, 500, 250);
curveBox.Image = xyCords;
how is it possible later if I made a new Graphics I append it like:
curveBox.Image += newGraphic;
** Please let me know if there is any component or something which already can do what I want. or else show me a good approach! Thanks!
Try using Graphics.DrawCurve. You just put all of the points you want in an array, and pass that and a pen to the method.
Edit:
Add this after your code to prove to yourself that both graphs coexist. To erase one or the other, just plot the same points, but in the background color of the bitmap (test for it, I don't remember what it is).
Point[] ptarray = new Point[3];
ptarray[0] = new Point(250, 250);
ptarray[1] = new Point(300, 300);
ptarray[2] = new Point(350, 400);
Pen pengraph = new Pen(Color.Green, 0.75F);
g.DrawCurve(pengraph, ptarray);
Point[] ptarray2 = new Point[3];
ptarray2[0] = new Point(100, 100);
ptarray2[1] = new Point(200, 150);
ptarray2[2] = new Point(250, 250);
Pen pengraph2 = new Pen(Color.Yellow, 1.25F);
g.DrawCurve(pengraph2, ptarray2);

Merging Shapes and Attaching Partial Drag/Drop in WPF

I would like to draw two shapes in WPF and merge them together. Then, I'd like to attach a drag/drop event to ONE of the original shapes.
So basically, you can only drag if you click on a certain part of the shape, but it will drag the entire shape with you.
Here is some code:
// Set up some basic properties for the two ellipses
Point centerPoint = new Point(100, 100);
SolidColorBrush ellipseColor_1 = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
double width_1 = 10; double height_1 = 10;
SolidColorBrush ellipseColor_2 = new SolidColorBrush(Color.FromArgb(50, 255, 0, 0));
double width_2 = 200; double height_2 = 200;
// Create the first ellipse: A small blue dot
// Then position it in the correct location (centerPoint)
Ellipse ellipse_1 = new Ellipse() { Fill = ellipseColor_1, Width = width_1, Height = height_1 };
ellipse_1.RenderTransform = new TranslateTransform(point.X - width_1 / 2, point.Y - height_1 / 2);
// Create the second ellipse: A large red, semi-transparent circle
// Then position it in the correct location (centerPoint)
Ellipse ellipse_2 = new Ellipse() { Fill = ellipseColor_2, Width = width_2, Height = height_2 };
ellipse_2.RenderTransform = new TranslateTransform(point.X - width_2 / 2, point.Y - height_2 / 2);
// ???
// How should I merge these?
// ???
// Now apply drag drop behavior to ONLY ellipse_1
MouseDragElementBehavior dragBehavior = new MouseDragElementBehavior();
dragBehavior.Attach(ellipse_1); // This may change depending on the above
// ...
// Add new element to canvas
This code creates two circles (a big one and a small one). I would like to only be able to drag if the small one is clicked, but I'd like to have them attached so they'll move together without having to manually add code that will take care of this.
If you put them both in a Grid (or Canvas, StackPanel, etc.), and set the drag behavior on the panel, they will be "merged". If you set IsHitTestVisible to false on ellipse_2, it won't respond to any mouse events, so effectively it won't be draggable.

Displaying rectangles in game window with XNA

I want to divide my game grid into an array of rectangles. Each rectangle is 40x40 and there are 14 rectangles in every column, with a total of 25 columns. This covers a game area of 560x1000.
This is the code I have set up to make the first column of rectangles on the game grid:
Rectangle[] gameTiles = new Rectangle[15];
for (int i = 0; i <= 15; i++)
{
gameTiles[i] = new Rectangle(0, i * 40, 40, 40);
}
I'm pretty sure this works, but of course I cannot confirm it because rectangles do not render on the screen for me to physically see them. What I would like to do for debugging purposes is to render a border, or fill the rectangle with color so I can see it on the game itself, just to make sure this works.
Is there a way to make this happen? Or any relatively simple way I can just make sure that this works?
Thank you very much.
First, make a 1x1 pixel texture of white for the rectangle:
var t = new Texture2D(GraphicsDevice, 1, 1);
t.SetData(new[] { Color.White });
Now, you need to render the rectangle - assume the Rectangle is called rectangle. For a rendering a filled block, it is very simple - make sure to set the tint Color to be the colour you want. Just use this code:
spriteBatch.Draw(t, rectangle, Color.Black);
For a border, is it more complex. You have to draw the 4 lines that make up the outline (the rectangle here is r):
int bw = 2; // Border width
spriteBatch.Draw(t, new Rectangle(r.Left, r.Top, bw, r.Height), Color.Black); // Left
spriteBatch.Draw(t, new Rectangle(r.Right, r.Top, bw, r.Height), Color.Black); // Right
spriteBatch.Draw(t, new Rectangle(r.Left, r.Top, r.Width , bw), Color.Black); // Top
spriteBatch.Draw(t, new Rectangle(r.Left, r.Bottom, r.Width, bw), Color.Black); // Bottom
Hope it helps!
This worked perfect if you want to draw rectangles over your existing textures. Great when you want to test/see for collisions
http://bluelinegamestudios.com/blog/posts/drawing-a-hollow-rectangle-border-in-xna-4-0/
-----From Site-----
The basic trick to drawing shapes is to make a single-pixel texture which is White, which you can then mix with other colors and display in solid shapes.
// At the top of your class:
Texture2D pixel;
// Somewhere in your LoadContent() method:
pixel = new Texture2D(GameBase.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
pixel.SetData(new[] { Color.White }); // so that we can draw whatever color we want on top of it
Then in your Draw() method do something like:
spriteBatch.Begin();
// Create any rectangle you want. Here we'll use the TitleSafeArea for fun.
Rectangle titleSafeRectangle = GraphicsDevice.Viewport.TitleSafeArea;
// Call our method (also defined in this blog-post)
DrawBorder(titleSafeRectangle, 5, Color.Red);
spriteBatch.End();
And the actual method that does the drawing:
private void DrawBorder(Rectangle rectangleToDraw, int thicknessOfBorder, Color borderColor)
{
// Draw top line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X, rectangleToDraw.Y, rectangleToDraw.Width, thicknessOfBorder), borderColor);
// Draw left line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X, rectangleToDraw.Y, thicknessOfBorder, rectangleToDraw.Height), borderColor);
// Draw right line
spriteBatch.Draw(pixel, new Rectangle((rectangleToDraw.X + rectangleToDraw.Width - thicknessOfBorder),
rectangleToDraw.Y,
thicknessOfBorder,
rectangleToDraw.Height), borderColor);
// Draw bottom line
spriteBatch.Draw(pixel, new Rectangle(rectangleToDraw.X,
rectangleToDraw.Y + rectangleToDraw.Height - thicknessOfBorder,
rectangleToDraw.Width,
thicknessOfBorder), borderColor);
}

Categories

Resources