I'm running into a problem with WPF and PathGeometry. I have the following Path object which is a combination of lines and arcs.
<Path Data="M100,180L220,180 M220,180L220,152 M220,152L217,150
M217,150L182,150 M180,147L180,132 M182,130L217,130 M217,130L220,127
M220,127L220,80 M220,80L100,80 M100,80L100,180 M182,150L180,147
M180,132L182,130">
Here's a simple code snippet that should draw the entire shape.
private void DrawIt()
{
Canvas canv = new Canvas();
this.Content = canv;
canv.Margin = new Thickness(0, 0, 0, 0);
canv.Background = new SolidColorBrush(Colors.White);
Path testPath = GetPath("blue");
testPath.Data = Geometry.Parse("M100,180L220,180 M220,180L220,152 M220,152L217,150 M217,150L182,150 M180,147L180,132 M182,130L217,130 M217,130L220,127 M220,127L220,80 M220,80L100,80 M100,80L100,180 M182,150L180,147 M180,132L182,130 ");
var area = testPath.Data.GetArea();
canv.Children.Add(testPath);
this.Content = canv;
}
private Path GetPath(string aColor)
{
Path imagePath = new Path();
SolidColorBrush colorBrush = (SolidColorBrush)new BrushConverter().ConvertFromString(aColor);
imagePath.Stroke = colorBrush;
imagePath.StrokeThickness = 2;
imagePath.Fill = colorBrush;
return imagePath;
}
The code above results in the following image:
My problem is that I'm trying to get the area of this image, but Path.Data.GetArea() only ever returns 0.0.
Any help would be appreciated.
You're describing multiple figures, all of them just single straight lines, because you keep issuing Move commands. If your path data was like this:
M100,180L220,180 220,152 217,150 182,150 180,147 180,132 182,130 217,130 220,127
220,80 100,80 Z
It instead describes a single figure that should draw the same shape you've shown but should also be fillable.
See Path Markup Syntax
Related
I'm using GMaps.NET and GMaps.WindowsPresentation in a WPF application. One of the classes that comes with GMaps presentation is GMapPolygon and a function called CreatePath. I'm trying to test adding a line between two or multiple points using this function but I'm getting the error when parsing a list of points. Must it be a certain type of list or am I missing something.
There are no examples that I can find of plotting a line between two points in using Windows Presentation but there are for Windows Forms
This is what I'm trying to do now:
List<PointLatLng> points = new List<PointLatLng>();
points.Add(new PointLatLng(48.866383, 2.323575));
points.Add(new PointLatLng(48.863868, 2.321554));
points.Add(new PointLatLng(48.861017, 2.330030));
points.Add(new PointLatLng(48.863727, 2.331918));
GMapPolygon SectorPolygon = new GMapPolygon(points);
SectorPolygon.CreatePath( points,true);
This is the function I'm parsing to:
public virtual Path CreatePath(List<Point> localPath, bool addBlurEffect)
{
// Create a StreamGeometry to use to specify myPath.
StreamGeometry geometry = new StreamGeometry();
using(StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(localPath[0], true, true);
// Draw a line to the next specified point.
ctx.PolyLineTo(localPath, true, true);
}
// Freeze the geometry (make it unmodifiable)
// for additional performance benefits.
geometry.Freeze();
// Create a path to draw a geometry with.
Path myPath = new Path();
{
// Specify the shape of the Path using the StreamGeometry.
myPath.Data = geometry;
if(addBlurEffect)
{
BlurEffect ef = new BlurEffect();
{
ef.KernelType = KernelType.Gaussian;
ef.Radius = 3.0;
ef.RenderingBias = RenderingBias.Performance;
}
myPath.Effect = ef;
}
myPath.Stroke = Brushes.MidnightBlue;
myPath.StrokeThickness = 5;
myPath.StrokeLineJoin = PenLineJoin.Round;
myPath.StrokeStartLineCap = PenLineCap.Triangle;
myPath.StrokeEndLineCap = PenLineCap.Square;
myPath.Fill = Brushes.AliceBlue;
myPath.Opacity = 0.6;
myPath.IsHitTestVisible = false;
}
return myPath;
}
I'm working on a mini-game where I need to make images go from their initial position to another using a translation transformation.
The problem is: I don't know how to proceed to apply the translation.
So here's the code used to generate my images.
Image myImage1 = new Image();
myImage1.Source = new BitmapImage(new Uri("/Images/image1.png", UriKind.Relative));
myImage1.Name = "image" + index++.ToString();
myImage1.Tap += myImage_Tap;
Canvas.SetLeft(image, 200);
Canvas.SetTop(image, 600);
gameArea.Children.Add(image);
Thanks for your time.
You have two choices to move your image around. The first is using a Canvas like your code shows, but you have to make sure your element is actually within a Canvas. Is your "gameArea" a Canvas? If it's not, your code won't work. The other option is to use Transforms.
var myImage1 = new Image
{
Source = new BitmapImage(new Uri("/Images/image1.png", UriKind.Relative)),
Name = "image" + index++.ToString(),
Tap += myImage_Tap,
RenderTransform = new TranslateTransform
{
X = 200,
Y = 600
}
};
gameArea.Children.Add(image);
Now gameArea can be any type of Panel and it will work.
Note that when using a Canvas, your Top and Left will be from the upper left corner of the Canvas. When using a Transform, your X and Y will be relative to where the element would have originally be drawn.
UPDATE -- Helper class to do simple animations using a Canvas
public sealed class ElementAnimator
{
private readonly UIElement _element;
public ElementAnimator(UIElement element)
{
if (null == element)
{
throw new ArgumentNullException("element", "Element can't be null.");
}
_element = element;
}
public void AnimateToPoint(Point point, int durationInMilliseconds = 300)
{
var duration = new Duration(TimeSpan.FromMilliseconds(durationInMilliseconds));
var easing = new BackEase
{
Amplitude = .3
};
var sb = new Storyboard
{
Duration = duration
};
var animateLeft = new DoubleAnimation
{
From = Canvas.GetLeft(_element),
To = point.X,
Duration = duration,
EasingFunction = easing,
};
var animateTop = new DoubleAnimation
{
From = Canvas.GetTop(_element),
To = point.Y,
Duration = duration,
EasingFunction = easing,
};
Storyboard.SetTargetProperty(animateLeft, "(Canvas.Left)");
Storyboard.SetTarget(animateLeft, _element);
Storyboard.SetTargetProperty(animateTop, "(Canvas.Top)");
Storyboard.SetTarget(animateTop, _element);
sb.Children.Add(animateLeft);
sb.Children.Add(animateTop);
sb.Begin();
}
}
So here's what I did with your code, I took just this part and made it a function:
public void AnimateToPoint(UIElement image, Point point, int durationInMilliseconds = 300)
{
var duration = new Duration(TimeSpan.FromMilliseconds(durationInMilliseconds));
var sb = new Storyboard
{
Duration = duration
};
var animateTop = new DoubleAnimation
{
From = Canvas.GetTop(image),
To = point.Y,
Duration = duration
};
Storyboard.SetTargetProperty(animateTop, new PropertyPath("Canvas.Top")); // You can see that I made some change here because I had an error with what you gave me
Storyboard.SetTarget(animateTop, image);
sb.Children.Add(animateTop);
sb.Begin();
}
And then I made the call with:
Point myPoint = new Point(leftpos, 300); // leftpos is a variable generated randomly
AnimateToPoint(myImage, myPoint);
So now there is still a single error, and it's in the instruction "sb.Begin;".
And it says: Cannot resolve TargetProperty Canvas.Top on specified object.
I reaylly don't know how to proceed.
Thanks for your previous answer!
I have a Windows Forms app where I add different figures(rectangles, circles, etc.) to the main form. The figure is a UserControl and it's shape I define with GraphicsPath.
Method for adding new figure:
void AddElement(ShapeType shape, string guid)
{
Shape newShape = new Shape();
newShape.Name = guid;
newShape.Size = new Size(100, 100);
newShape.Type = shape;
newShape.Location = new Point(100, 100);
newShape.MouseDown += new MouseEventHandler(Shape_MouseDown);
newShape.MouseMove += new MouseEventHandler(Shape_MouseMove);
newShape.MouseUp += new MouseEventHandler(Shape_MouseUp);
newShape.BackColor = this.BackColor;
this.Controls.Add(newShape);
}
In Shape (Figure) class:
private ShapeType shape;
private GraphicsPath path = null;
public ShapeType Type
{
get { return shape; }
set
{
shape = value;
DrawElement();
}
}
private void DrawElement()
{
path = new GraphicsPath();
switch (shape)
{
case ShapeType.Rectangle:
path.AddRectangle(this.ClientRectangle);
break;
case ShapeType.Circle:
path.AddEllipse(this.ClientRectangle);
break;
case ShapeType.Line:
path.AddLine(10,10,20,20);
break;
}
this.Region = new Region(path);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if (path != null)
{
e.Graphics.DrawPath(new Pen(Color.Black, 4), path);
}
}
When resizing the figure, I redraw It:
protected override void OnResize(System.EventArgs e)
{
DrawElement();
this.Invalidate();
}
Everything works fine when I add shapes like rectangle and circle. But when I choose Line, nothing appears on my form. The breakpoint shows that the programs steps in all the methods and this.Controls.Add(newShape); as well.
I do not understand why this is not working.
I'd appreciate any advice.
You can draw an open GraphicsPath with a thin or a thick Pen. But a region must be set from a closed shape or else there is no place where your pixels could show up. This will help to keep your region intact; but you need to know, just what you want it to be:
if (shape != ShapeType.Line) this.Region = new Region(path);
If you want it to be something like a thick line you must create a polygon or a series of lines to outline the shape you want. And if you want your line to be inside that region you will need two paths: one closed polygon path to set the region and one open line path to draw the line inside the region.
Edit:
The best way to create the closed path is probably to use the Widen() method with the Pen you are using like this:
GraphicsPath path2 = path.Widen(yourPen);
This would get the thickness right as well as the line caps and also work for more complicated polylines; I haven't tried it though..
Maybe it's because the line has no area. Try to replace it with a very thin shape having a positive area. For instance:
const int thickness = 1;
path.AddLines(new[]
{
new Point(10, 10),
new Point(20, 20),
new Point(20 + thickness, 20 + thickness),
new Point(10 + thickness, 10 + thickness)
});
I am trying to use a MapPolyLine in my Map to show a real-time route, hopefully it will move/scale this time. The thing is the line is not being shown on the map, and I cannot find any programming mistake:
C#
MapLayer pathLayer;
//Constructor
pathLayer = new MapLayer();
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 10;
//line.Path.Add(several points); Tested, no effect
MapOverlay overlay = new MapOverlay();
overlay.Content = line;
//overlay.GeoCoordinate = new GeoCoordinate(0,0); Tested, no effect
//overlay.PositionOrigin = new Point(0.0, 1.0); Tested, no effect
pathLayer.Add(overlay);
MyMap.Layers.Add(pathLayer);
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
MapPolyline line = pathLayer.First(TrackPath).Content as MapPolyline;
line.Path.Add(args.Position.Coordinate); // Checked values, line.Path adds them correctly
}
EDIT: New info. The emulator shows an error when trying to add it using XAML, and the emulator shows the name of the class on the top of the map as a graphic glitch:
MapPolylines and MapPolygons should be added to the MapElements collection... not a MapLayer or a MapOverlay.
You should be able to make this example work for you.
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 10;
line.Path.Add(new GeoCoordinate(47.6602, -122.098358));
line.Path.Add(new GeoCoordinate(47.561482, -122.071544));
MyMap.MapElements.Add(line);
In your GeoCoord watcher you'll have to get the line from the map's MapElements collection, and add the new position to the line's path instead of predefining like I did. This should be doable.
In Windows Phone 8.1 try add points in this way. "punkty" is my collection.
List<BasicGeoposition> PosList = new List<BasicGeoposition>();
foreach (var item in punkty)
{
PosList.Add(new BasicGeoposition()
{
Latitude = item.Position.Latitude,
Longitude = item.Position.Longitude
});
}
//Example of one point
//PosList.Add(new BasicGeoposition()
//{
// Latitude = 52.46479093,
// Longitude = 16.91743341
//});
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Red;
line.StrokeThickness = 5;
line.Path = new Geopath(PosList);
myMap.MapElements.Add(line);
An image (Image class) is placed above a SMFPlayer (both elements are created in code-behind). Z-index of the image is the Z-Index of SMFPlayer + 1. The image is resized (adjusting the width) according to the playing progress of SMFPlayer.
videoPlayer = new SMFPlayer();
videoPlayer.Width = 1920;
videoPlayer.Height = 1080;
videoPlayer.Margin = new Thickness(1920, 0, 0, 0);
PlaylistItem item = new PlaylistItem();
Random r = new Random();
item.MediaSource = new Uri("video.wmv");
item.DeliveryMethod = DeliveryMethods.ProgressiveDownload;
videoPlayer.Playlist.Add(item);
videoPlayer.AutoPlay = true;
videoPlayer.AutoLoad = true;
videoPlayer.IsControlStripVisible = false;
videoPlayer.PlaylistVisibility = FeatureVisibility.Disabled;
videoPlayer.MediaEnded += new EventHandler(player_MediaEnded);
LayoutRoot.Children.Add(videoPlayer);
bar_yellow3 = new Image();
bar_yellow3.Source = new BitmapImage(new Uri("/SMF_ProgressiveDownload1;component/assets/bar_y.png", UriKind.Relative));
bar_yellow3.Width = 775;
bar_yellow3.Height = 34;
bar_yellow3.Margin = new Thickness(2948,1034,0,0);
bar_yellow3.Stretch = Stretch.Fill;
bar_yellow3.VerticalAlignment = VerticalAlignment.Top;
bar_yellow3.HorizontalAlignment = HorizontalAlignment.Left;
LayoutRoot.Children.Add(bar_yellow3);
However, when the playing progress is less than 20%, the image blinks randomly. When the SMFPlayer is set to be invisible ( Visibility.Collapsed ) , the image is normal.
I have tried to call the update function of the Image, which is: bar_yellow3.UpdateLayout(); but the method does not solve the blinking issue.
Any solution?
Try use effects (Shazzam will help you) instead using Z order.