Need to divide a UserControl with a background picture into multiple small clickable areas. Clicking them should simply raise an event, allowing to determine which particular area of the picture was clicked.
The obvious solution is using transparent labels. However, they are heavily flickering. So it looks like labels are not designed for this purpose, they take too much time to load.
So I'm thinking if any lighter option exists? To logically "slice up" the surface.
I also need a border around the areas, though.
on the user control do:
MouseClick += new System.Windows.Forms.MouseEventHandler(this.UserControl1_MouseClick);
and now in the UserControl1_MouseClick event do:
private void UserControl1_MouseClick(object sender, MouseEventArgs e)
{
int x = e.X;
int y = e.Y;
}
now let's divide the user control to a 10x10 area:
int xIdx = x / (Width / 10);
int yIdx = y / (Height / 10);
ClickOnArea(xIdx, yIdx);
in ClickOnArea method you just need to decide what to do in each area. maybe using a 2d array of Action
as for the border do this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Pen p = new Pen(Color.Black);
float xIdx = (float)(Width / 10.0);
float yIdx = (float)(Height / 10.0);
for (int i = 0; i < 10; i++)
{
float currVal = yIdx*i;
g.DrawLine(p, 0, currVal, Width, currVal);
}
g.DrawLine(p, 0, Height - 1, Width, Height - 1);
for (int j = 0; j < 10; j++)
{
float currVal = xIdx * j;
g.DrawLine(p, currVal, 0, currVal, Height);
}
g.DrawLine(p, Width - 1, 0, Width - 1, Height);
}
Related
Let's say I have a 1280x1024 BitmapSource in a WPF Image control. There is a 100x100 "active" area of this image that I want to be able to zoom in on with a button click. I want to zoom as much as possible while maintaining the aspect ratio and keeping all "active" pixels visible. This is what I have:
XAML:
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Zoom" Click="Button_Click" />
<Border DockPanel.Dock="Top" ClipToBounds="True">
<Image Name="TheImage" />
</Border>
</DockPanel>
Code:
private const int WIDTH = 1280;
private const int HEIGHT = 1024;
private const int MIN_X = 100;
private const int MAX_X = 200;
private const int MIN_Y = 100;
private const int MAX_Y = 200;
public MainWindow()
{
InitializeComponent();
byte[] image = new byte[WIDTH * HEIGHT];
for (int y = MIN_Y; y <= MAX_Y; y++)
for (int x = MIN_X; x <= MAX_X; x++)
image[y * WIDTH + x] = byte.MaxValue;
TheImage.Source = BitmapSource.Create(WIDTH, HEIGHT, 96.0, 96.0, PixelFormats.Gray8, null, image, WIDTH);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
double factor = 1.0 / Math.Max((MAX_X - MIN_X) / (double)WIDTH, (MAX_Y - MIN_Y) / (double)HEIGHT);
Matrix matrix = Matrix.Identity;
matrix.ScaleAt(factor, factor, (MIN_X + MAX_X) / 2.0 / WIDTH * TheImage.ActualWidth, (MIN_Y + MAX_Y) / 2.0 / HEIGHT * TheImage.ActualHeight);
TheImage.RenderTransform = new MatrixTransform(matrix);
}
Here is what it looks like before zoom:
And here is after zoom:
It looks like the amount of zoom is correct, but I think the issue is that the center of the scaling should shift due to the scaling, but I'm not sure how to account for that upfront.
Figured it out. The simplest approach seems to be to first do a translation such that the center of the active area is in the center of the image, then do the scaling centered on that:
private void Button_Click(object sender, RoutedEventArgs e)
{
double factor = 1.0 / Math.Max((MAX_X - MIN_X) / (double)WIDTH, (MAX_Y - MIN_Y) / (double)HEIGHT);
Matrix matrix = Matrix.Identity;
matrix.Translate(0.5 * TheImage.ActualWidth - (MIN_X + MAX_X) / 2.0 / WIDTH * TheImage.ActualWidth, 0.5 * TheImage.ActualHeight - (MIN_Y + MAX_Y) / 2.0 / HEIGHT * TheImage.ActualHeight);
matrix.ScaleAt(factor, factor, 0.5 * TheImage.ActualWidth, 0.5 * TheImage.ActualHeight);
TheImage.RenderTransform = new MatrixTransform(matrix);
}
Screenshot after zoom:
I would like to know where I can find 3 good algorithms, or 3 examples of C code to :
apply a sketch effect on an image (black and white)
apply a sketch effect on an image (color)
apply a cartoon effect on an image (color)
I already have a code for the black and white sketch, but it's too white..
void sketchFilter(int *pixels, int width, int height) {
changeImageToGray(pixels, width, height);
int *tempPixels = new int[width * height];
memcpy(tempPixels, pixels, width * height * sizeof(int));
int threshold = 7;
float num = 8;
for (int i = 1; i < height - 1; i++) {
for (int j = 1; j < width - 1; j++) {
Color centerColor(tempPixels[i * width + j]);
int centerGray = centerColor.R();
Color rightBottomColor(tempPixels[(i + 1) * width + j + 1]);
int rightBottomGray = rightBottomColor.R();
if (abs(centerGray - rightBottomGray) >= threshold) {
pixels[i * width + j] = RGB2Color(0, 0, 0); // black
}
else {
pixels[i * width + j] = RGB2Color(255, 255, 255); // white
}
}
}
delete [] tempPixels;
}
Any way to improve this code, or should I go with a completely different?
How can I do both color cartoon (doodle?) and color sketch?
Thanks!
The cartoon-like style is described in this page; it can be achieved via a smoothing followed by a(n aggressive) posterize effect. Both can be carried out using OpenCV, using any filter for the smotthing and PyrMeanShiftFiltering for the posterize.
Edit:
The pencil (colour) sketch is described f.e. in this StackOverflow question.
I am creating a document based application and i want to draw a horizontal line underlying the text. But, line should not be straight. i want to draw a line like this.
Currently i am using System.Graphics object to draw any object.
private void DrawLine(Graphics g, Point Location, int iWidth)
{
iWidth = Convert.ToInt16(iWidth / 2);
iWidth = iWidth * 2;
Point[] pArray = new Point[Convert.ToInt16(iWidth / 2)];
int iNag = 2;
for (int i = 0; i < iWidth; i+=2)
{
pArray[(i / 2)] = new Point(Location.X + i , Location.Y + iNag);
if (iNag == 0)
iNag = 2;
else
iNag = 0;
}
g.DrawLines(Pens.Black, pArray);
}
UPDATED:
Above code is working fine and line draws perfectly but, this code effects on application performance. Is there another way to do this thing.
If you want fast drawing just make a png image of the line you want, with width larger than you need and then draw the image:
private void DrawLine(Graphics g, Point Location, int iWidth)
{
Rectangle srcRect = new Rectangle(0, 0, iWidth, zigzagLine.Height);
Rectangle dstRect = new Rectangle(Location.X, Location.Y, iWidth, zigzagLine.Height);
g.DrawImage(zigzagLine, dstRect, srcRect, GraphicsUnit.Pixel);
}
zigzagLine is the bitmap.
valter
I have this texture representing internal area of a pipe:
I have created a simple cylinder model (GeometryModel3D) by dissecting the above texture to a grid of points and than wrapping this to form a cylinder, then mapping the above texture to it (each square on a grid having two triangles):
The left and right edges of the texture meet at the above black seam line, which is unwanted.
I could not use single vertex point for smooth continuation there, because that point corresponds to both 0 and 360 degrees which is single angle, but two distinct points on edges of the 2D texture.
So I have used pairs of vertices on the same location but each corresponding to different point in the source texture.
Is there anything I can do to hide the seam line?
Model creation code:
private void SceneViewerWindowOnLoaded(object sender, RoutedEventArgs e)
{
var imageSource = new BitmapImage(new Uri("pack://application:,,,/Resources/out1.jpg"));
var meshGeometry3D = new MeshGeometry3D();
double circumference = imageSource.PixelWidth;
double radius = (circumference / (2.0 * Math.PI));
double angleSegment = (2.0 * Math.PI / SegmentCountTrans);
double sizeSegmentLong = ((double)imageSource.PixelHeight / SegmentCountLong);
double sizeSegmentTrans = ((double)imageSource.PixelWidth / SegmentCountTrans);
for (int indexSegmentLong = 0; indexSegmentLong < SegmentCountLong; indexSegmentLong++)
{
double depth = (indexSegmentLong * sizeSegmentLong);
for (int indexSegmentTrans = 0; indexSegmentTrans < SegmentCountTrans; indexSegmentTrans++)
{
meshGeometry3D.Positions.Add(GetVertexPosition(indexSegmentTrans, depth, radius, angleSegment));
meshGeometry3D.TextureCoordinates.Add(new Point(indexSegmentTrans * sizeSegmentTrans, depth));
}
// add one extra vertex representing 360 degrees for complete wrap-around
meshGeometry3D.Positions.Add(GetVertexPosition(SegmentCountTrans, depth, radius, angleSegment));
meshGeometry3D.TextureCoordinates.Add(new Point(SegmentCountTrans * sizeSegmentTrans, depth));
}
meshGeometry3D.TriangleIndices.Clear();
for (int indexSegmentLong = 0; indexSegmentLong < (SegmentCountLong - 1); indexSegmentLong++)
{
for (int indexSegmentTrans = 0; indexSegmentTrans < SegmentCountTrans; indexSegmentTrans++)
{
int indexCurrent = (indexSegmentLong * (SegmentCountTrans + 1) + indexSegmentTrans);
meshGeometry3D.TriangleIndices.Add(indexCurrent);
meshGeometry3D.TriangleIndices.Add(indexCurrent + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 2);
}
}
var geometryModel3D = new GeometryModel3D
{
Geometry = meshGeometry3D,
Material = new DiffuseMaterial
{
Brush = new ImageBrush(imageSource)
}
};
this.SceneViewer.Model = geometryModel3D;
}
private static Point3D GetVertexPosition(int indexSegmentAngle, double depth, double radius, double angleSegment)
{
double angle = (StartAngle + indexSegmentAngle * angleSegment);
return new Point3D(
radius * Math.Cos(angle),
radius * Math.Sin(angle),
-depth);
}
XAML code for the Viewport3D element containing the model:
<Viewport3D x:Name="Viewport3D">
<Viewport3D.Camera>
<PerspectiveCamera x:Name="Camera" Position="0.0,0.0,0.0" LookDirection="0.0,0.0,-1.0" UpDirection="0.0,1.0,0.0" FieldOfView="90"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D x:Name="ModelVisualModel"/>
</Viewport3D>
When you create your image brush try setting the TileMode to TileMode.Tile and ViewportUnits to BrushMappingMode.Absolute. If that still doesn't work then post a follow-up comment here and I'll try to reproduce it on my end.
I used DrawingBrush instead of Imagebrush. This fixed the seams and I also get an fps boost when setting the cachinghint.
Brush brush = new DrawingBrush(new ImageDrawing(_texture, new Rect(0,0,1,1)));
RenderOptions.SetCachingHint(brush , CachingHint.Cache);
RenderOptions.SetCacheInvalidationThresholdMinimum(brush , 0.5);
RenderOptions.SetCacheInvalidationThresholdMaximum(brush , 2.0);
In C#, WPF I've created a rectangle:
Rectangle myRgbRectangle = new Rectangle();
myRgbRectangle.Width = 1;
myRgbRectangle.Height = 1;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
Yes, I really just want it to be 1 pixel by 1 pixel. And I want to change the color based on the variable height like so:
mySolidColorBrush.Color = Color.FromArgb(255, 0, 0, (byte)height);
myRgbRectangle.Fill = mySolidColorBrush;
Now, how do I draw at a specific x,y location on the screen? I do have a grid (myGrid) on my MainWindow.xaml.
Thanks!
Here's the pertinent code:
myRgbRectangle.Width = 1;
myRgbRectangle.Height = 1;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
int height;
for (int i = 0; i < ElevationManager.Instance.heightData.GetLength(0); i++)
for (int j = 0; j < ElevationManager.Instance.heightData.GetLength(1); j++)
{
height = ElevationManager.Instance.heightData[i, j] / 100;
// Describes the brush's color using RGB values.
// Each value has a range of 0-255.
mySolidColorBrush.Color = Color.FromArgb(255, 0, 0, (byte)height);
myRgbRectangle.Fill = mySolidColorBrush;
myCanvas.Children.Add(myRgbRectangle);
Canvas.SetTop(myRgbRectangle, j);
Canvas.SetLeft(myRgbRectangle, i);
And it's throwing this error: Specified Visual is already a child of another Visual or the root of a CompositionTarget.
You need to use a Canvas istead of a Grid. You use coordinates to position elements in a Canvas versus Column and Row in a Grid.
Definition of a Canvas:
Defines an area within which you can explicitly position child elements by using coordinates that are relative to the Canvas area.
You would then use Canvas.SetTop and Canvas.SetLeft Properties like this (assuming that your canvas is named myCanvas):
myCanvas.Children.Add(myRgbRectangle);
Canvas.SetTop(myRgbRectangle, 50);
Canvas.SetLeft(myRgbRectangle, 50);
Edit
Based on your edit, it is like I said you are adding the same rectangle more than once. You need to be creating it in your For Loop each time you add it. Something like this.
for (int i = 0; i < ElevationManager.Instance.heightData.GetLength(0); i++)
for (int j = 0; j < ElevationManager.Instance.heightData.GetLength(1); j++)
{
Rectangle rect = new Rectangle();
rect.Width = 1;
rect.Height = 1;
height = ElevationManager.Instance.heightData[i, j] / 100;
// Describes the brush's color using RGB values.
// Each value has a range of 0-255.
mySolidColorBrush.Color = Color.FromArgb(255, 0, 0, (byte)height);
rect.Fill = mySolidColorBrush;
myCanvas.Children.Add(rect);
Canvas.SetTop(rect, j);
Canvas.SetLeft(rect, i);
}