I'm using coordinates on absolute layout to mark some points i.e.:
new Point() { X = 0, Y = 0 };
new Point() { X = 0, Y = 300 };
new Point() { X = 200, Y = 0 };
How to draw a lines from point to point, connect them, in this case triangle shape?
SkiaSharp is great, but if you do not need all of Skia's features, the overhead of native libraries, increased app size, init times, and memory consumption can be avoided with NControl/NGraphics.
In terms of drawing a path of points, here is a really quick example:
NControlView Subclass that draws a list of NGraphics' PathOp:
public class PathControl : NControlView
{
public IEnumerable<PathOp> Path { get; set; }
public override void Draw(ICanvas canvas, Rect rect)
{
if (Path != null)
canvas.DrawPath(Path, new Pen(Colors.Red, 5));
}
}
Runtime assignment:
You can then assign the IEnumerable<PathOp> at runtime:
pathControl.Path = new PathOp[] {
new MoveTo (40, 60),
new LineTo (120, 60),
new LineTo (80, 30),
new ClosePath ()
};
pathControl.Invalidate();
Xaml:
Taking the XAML from this answer and adding:
<nc:PathControl x:Name="pathControl" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All" />
You can produce this:
With more vector work, something like this:
Other NControl/NGraphics answers/examples:
re: Xamarin.Forms - How To Achieve 45 deg. Angled Background Color
re: Creating completeness meter (Status display) in Xamarin
re: Pop Up in xamarin.forms
Related
I am now really desperate. I just can't get any further and in principle it shouldn't be that difficult. I tried solving it the last 2 weeks.
The following task/problem:
I have a canvas in my C# WPF project called “MarkPointsMap”, on which there are different kind of mark points (3 different kind of rectangles with different colors). One MarkPointsMap can contain round about 3000-4000 different MarkPoints (Children of that canvas) all over across the MarkPointsMap.
The positions of that MarkPoints are loaded from a .tab-file, which is converted into a list called “MarkPoints” and added as Children on the “MarkPointsMap” canvas after loading the respective .tab-file.
This a screenshot of the MarkPointsMap:
There are e.g. mostly small rectangles (the green ones), a few middle rectangles (the yellow ones) and two big rectangles (the red ones). Mulitple markpoints should be selected with a selection-rectangle. In my project I can already draw the selection rectangle, but the “if this.selectRect.Contains(MarkPointsMap.Children)” (see below) part does not work.
The selection-rectangle looks as follows:
The following method “drawMarkPoints”, which is called after loading the .tab file in the program, draws the markpoints on the canvas “MarkPointsMap”:
public void drawMarkPoints()
{
MarkPointsMap.Children.Clear();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
if (markpoint.type.relevant)
{
Rectangle markpointArea = new Rectangle();
markpointArea.Tag = markpoint;
//!!
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = markpoint.fillColor;
markpointArea.Fill = mySolidColorBrush;
markpointArea.StrokeThickness = 0.2;
markpointArea.Stroke = Brushes.Black;
markpointArea.Width = markpoint.symbol.Width;
markpointArea.Height = markpoint.symbol.Height;
//set the markpoints in canvas
Canvas.SetLeft(markpointArea, getCanvasCoordinates(markpoint.center).X - 0.5 * markpoint.symbol.Width);
Canvas.SetTop(markpointArea, getCanvasCoordinates(markpoint.center).Y - 0.5 * markpoint.symbol.Height);
MarkPointsMap.Children.Add(markpointArea);
}
}
}
The selection-rectangle is drawn in the canvas “MarkPointsMap” with the three Mouse-Events (MarkPointsMap_MouseDown, MarkPointsMap_MouseMove, MarkPointsMap_MouseUp):
public Rect itemRect;
public Rect selectRect;
private point anchorPoint;
private Rectangle manualDragRect = new Rectangle();
private void MarkPointsMap_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
isLeftMouseButtonDownOnWindow = true;
anchorPoint = e.MouseDevice.GetPosition(MarkPointsMap);
MarkPointsMap.CaptureMouse();
e.Handled = true;
manualDragRect = new Rectangle
{
Stroke = Brushes.Red,
StrokeThickness = 2
};
MarkPointsMap.Children.Add(manualDragRect);
}
}
private void MarkPointsMap_MouseMove(object sender, MouseEventArgs e)
{
if (!MarkPointsMap.IsMouseCaptured)
return;
Point location = e.MouseDevice.GetPosition(MarkPointsMap);
double minX = Math.Min(location.X, anchorPoint.X);
double minY = Math.Min(location.Y, anchorPoint.Y);
double maxX = Math.Max(location.X, anchorPoint.X);
double maxY = Math.Max(location.Y, anchorPoint.Y);
Canvas.SetTop(manualDragRect, minY);
Canvas.SetLeft(manualDragRect, minX);
double height = maxY - minY;
double width = maxX - minX;
manualDragRect.Height = Math.Abs(height);
manualDragRect.Width = Math.Abs(width);
Point xy_2 = new Point((Canvas.GetLeft(manualDragRect) + 0.5 * width), (Canvas.GetTop(manualDragRect) + 0.5 * height));
this.selectRect = new Rect(xy_2.X, xy_2.Y, width, height);
}
private void MarkPointsMap _MouseUp(object sender, MouseButtonEventArgs e)
{
MarkPointsMap.ReleaseMouseCapture();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);
if (selectRect.Contains(itemRect))
{
MessageBox.Show("Test!"); // it does not reach this code, if I circle several markpoints with the red selection-rectangle called “selectRect”
}
}
}
Why doesn’t this work? I guess it has to do with the converting from rectangle (System.Windows.Shapes using derictive) to struct Rect : IFormattable.
The “if (selectRect.Contains(itemRect)”, which is not reached should in perspective color all mark points, which are inside the selection-rectangle in red, then compute the Point (x-Coordinate, y-Coordinate) of the middle-point of the selection-rectangle and add this middle point back to the original .tab-file.
Any ideas or hints, how I could continue? Thanks in advance.
Best regards!
It looks like your calculations are wrong. You are offsetting the selectRect. You are centering it relative to the rendered manualDragRect. This way you are testing a totally different area than the one you have defined by the rendered selection bounds.
Additionally, you are capturing the mouse on the Canvas but release the capture on some different control. It should be released on the MarkPointsMap too. Also be careful with marking mouse events as handled. If the input handling is not part of a custom control, you should generally avoid it.
You should consider to use an ItemsControl with a Canvas as panel. You can then move the drawing to the markup and define a DataTemplate for the MarkPoint data.
I recommend to use the original Rect returned from the manualDragRect shape. This means you should remove the selectRect field and related computations:
private void MarkPointsMap_MouseUp(object sender, MouseButtonEventArgs e)
{
MarkPointsMap.ReleaseMouseCapture();
foreach (MarkPoint markpoint in this.Marks.markpoints)
{
Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);
Rect selectionBounds = manualDragRect.RenderedGeometry.Bounds;
selectionBounds.Offset(Canvas.GetLeft(manualDragRect), Canvas.GetTop(manualDragRect));
if (selectionBounds.Contains(itemRect))
{
MessageBox.Show("Test!");
}
// TODO::Calculate center of selectionBounds
Point selectionBoundsCenter = ...;
}
}
I'm trying to dynamically draw lines by using the package SkiaSharp.
I've defined the control in my xaml like this:
<skia:SKCanvasView x:Name="CanvasView" PaintSurface="OnCanvasViewPaintSurface" />
This is my code behind class:
private SkiaSharp.SKCanvas canvas;
private SkiaSharp.SKSurface surface;
void OnCanvasViewPaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs args)
{
SkiaSharp.SKImageInfo info = args.Info;
surface = args.Surface;
canvas = surface.Canvas;
canvas.Clear();
SkiaSharp.SKPaint thinLinePaint = new SkiaSharp.SKPaint
{
Style = SkiaSharp.SKPaintStyle.Stroke,
Color = SkiaSharp.SKColors.Blue,
StrokeWidth = 6
};
canvas.DrawLine(0, 0, 50, 50, thinLinePaint);
}
The part above works fine, and a blue line will be drawn when loading the view at startup. But what I want to do is to dynamically draw new lines and remove the old ones.
public void DrawNewLine()
{
canvas.Clear();
SkiaSharp.SKPaint thickLinePaint = new SkiaSharp.SKPaint
{
Style = SkiaSharp.SKPaintStyle.Stroke,
Color = SkiaSharp.SKColors.Red,
StrokeWidth = 16
};
canvas.DrawLine(0, 0, 50, 50, thickLinePaint);
}
I am using the canvas field that was declared before, but it's not working. Application will crash at runtime when using the canvas object.
What am I doing wrong?
You need to use SKCanvasView.InvalidateSurface() method to recall OnCanvasViewPaintSurface() internally.
It seems like this should be simple enough, but I'm really struggling with finding any documentation on how I can do this. I'm simply looking to crop an image to turn a square into a circle.
There is a lot of discussion about it, but I can't seem to find a good example of how to do this using UWP/Win2D.
Here is a bit of code to illustrate the issue I was trying to describe in my comments:
// draw a 10x10 grid of circles
var bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/ice.png"); // hex-shaped image is 250x220 pixels
var brush = new CanvasImageBrush(sender, bitmap);
for (var i = 0; i < 10; i++)
{
for (var j = 0; j < 10; j++)
{
//_drawingSession.FillCircle(new Vector2(i * 50, j * 50), (float)(25), Colors.Blue);
_drawingSession.FillCircle(new Vector2(i * 50, j * 50), (float)(25), brush);
}
}
The image below shows how the brush is being cut from the same x/y coordinates based on the vector where the target circle is to be drawn.
Note: the same effect occurs with FillEllipse().
You can try to use CanvasImageBrush and CanvasDrawingSession.FillEllipse Method achieve it.
private async void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
using (CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/image.jpg"))
{
CanvasImageBrush canvasImageBrush = new CanvasImageBrush(sender, bitmap);
args.DrawingSession.FillEllipse(new System.Numerics.Vector2(100f), 100, 100, canvasImageBrush);
}
}
------------ Update -------------
If you want to cut a circle out of the image source, you can configure the CanvasImageBrush.Transform property to scale the image, then cut the circle and display it on the canvas.
private async void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
using (CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/image.jpg"))
{
CanvasImageBrush canvasImageBrush = new CanvasImageBrush(sender, bitmap);
System.Numerics.Vector2 center = new System.Numerics.Vector2((float)(bitmap.Size.Width / 2),
(float)(bitmap.Size.Height / 2));
canvasImageBrush.Transform = System.Numerics.Matrix3x2.CreateScale(0.5F, center);
args.DrawingSession.FillEllipse(center, 160, 160, canvasImageBrush);
}
}
You should change some parameters in my above code to satisfy your requirement, such as the scale in the Matrix3x2.CreateScale method.
Okay, after chatting with one of the fellows on the GitHub Win2D project, I finally have a clear answer on how this works - and it works nothing like I would have expected it to work.
First, the bitmap brush image is by default being positioned at 0,0 on the canvas.
In my case, I wanted to cut a circle from the image and draw it someplace else on the canvas. This requires 2 separate bits of math.
First, you need to position the bitmap's top-left-corner (TLC) to where you want the circle to be drawn. This is done by setting the brush's Transform property. In my example, I'm setting the image TLC to 300/300;
// create the brush
var brush = new CanvasImageBrush(sender, _tiles[1]);
brush.Transform = Matrix3x2.CreateTranslation(300, 300);
Now, to cut/draw the circle using the brush image, I have to describe where the center of the image is to be on the canvas. My image is 250x220.
// 300+250/2, 300+220/2 = 425, 410
_args.DrawingSession.FillCircle(new Vector2(425, 410), (float)(110), brush);
This gives the effect of cutting a circle out of my original bitmap and drawing it on the canvas at the desired location.
Hopefully this is clear enough. I know I certainly struggled to find the answer.
I'm creating a simple 2d scrolling game as my school project on C#, but I cant seem to find the ratio between the background (platform) and the form height.
here's what I'm trying to do, my form height is 1080, and all of my pictures height are 2160px (double the form) with dpi of 96, I have the background, the floor, and the hero all on different image files so that I can move each one as I want.
I'm trying to get them all to start from the bottom so it would look like this
so I write this code.
void Start()
{
flag = 's';
bg = new Bitmap("bg.bmp");
pf = new Bitmap("platform.png");
player = new Bitmap("idle.png");
y = -(pf.Height-this.ClientSize.Height);
mmy = y;
DrawDubBuff(this.CreateGraphics());
}
void DrawScen(Graphics g)
{
g.Clear(Color.Black);
//SolidBrush br = new SolidBrush(Color.Yellow);
//g.FillRectangle(br, this.Width/2 - this.Height/4, 0, (this.Height/2), this.Height);
if (flag=='m')
{
g.DrawImage(bg, x, y);
g.DrawImage(menu, this.Width/2, this.Height/2);
}
else if (flag == 's')
{
g.DrawImage(bg, x, y);
g.DrawImage(pf, pfx, y);
g.DrawImage(player, mmx, mmy);
//Color pixel = pf.GetPixel(mmx, mmy+10);
MessageBox.Show(""+pf.Height+", "+ this.ClientSize.Height);
//if (pixel.A == 0)
//{
// mmy--;
//}
}
}
void DrawDubBuff(Graphics g)
{
Graphics g2 = Graphics.FromImage(off);
DrawScen(g2);
g.DrawImage(off, 0, 0);
}
but it ends up looking like this
the red square is exactly at half the image size (1080)
even when I change the image size to 1080 and make y=0, it still doesnt fit the form, it always goes beyond the screen size. I spent hours searching but I couldn't find anything that helped. really hope someone can help me with this, thanks in advance ~
UPDATE
this is what happens when I set y=-1080
If memory serves, 0,0 on a winforms/GDI canvas is top left, try setting y to 1080 rather than 0
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