I need to draw a sine curve (from x = 0 to 2pi) as part of a DrawingVisual and would like to use WPF's basic path capabilities to get a smooth curve. Probably I need some sort of bezier curve for that. Unfortunately I don't even know how they work. (Just that they can "pull" the line towards a control point somehow.)
Can somebody tell me what coordinates I should use to make it look right?
I could apply a ScaleTransform if I want to stretch it a little, so the normal form would be fine.
A thread in the MSDN forums just brought me in the middle of a formula mess in Wikipedia's scientific depths. I haven't studied maths so that's not much use to me.
Nevermind, I played a while with Kaxaml and found this pretty neat. It's probably a bit stretched already, but it makes the plot more recognisable.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Path Data="M0,100 L50,0 L50,200 L100,100" Stroke="Gray" StrokeThickness="0.5"/>
<Path Data="M0,100 C50,0 50,200 100,100" Stroke="Red" StrokeThickness="3"/>
</Grid>
</Page>
The first Path (grey) shows the control points used and how they are on the same horizontal offset; the second Path (red, bold) shows the final bezier curve.
This is how it looks like:
Related
Is there a way you can plot a graph/chart with WPF, I have a file with values, so I want the output to look like this:
This XAML code:
<Grid>
<ItemsControl ItemsSource="{Binding Lines}" Margin="44,102,40,205" Grid.RowSpan="2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="White"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X1="{Binding From.X}" Y1="{Binding From.Y}"
X2="{Binding To.X}" Y2="{Binding To.Y}"
Stroke="DarkGray" StrokeThickness="3"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
My ViewModel that should have all of the implementation drawing a graph:
My idea was to have a collection of lines, but I then froze, and couldn't implement a good solution.
I would read values from a txt file, but I do not know how should I pass them so they can be drawn on the graph.
public ObservableCollection<Line> Lines { get; private set; }
Lines = new ObservableCollection<Line>
{
// I would add new lines here
};
I have no experience when it comes to plotting graphs, so every advice would be a huge help to me
I don't think this is possible with just a simple use of the built-in controls in WPF, as you are trying to do. You need a custom control to actually render a usable chart. And take it from someone who has actually written a WPF line chart control from scratch: it's a real project. There are a lot of moving parts and things to consider.
Unless you really needed a super custom or proprietary implementation, you'd be far better off going with one of the libraries that have already been written. A quick google search for "WPF chart library" gives you a number of results. A free option that I've personally used is LiveCharts
That being said, if you really need to make your own, here are some points I had to cover in creating mine:
A function to return a "scale" (what numeric values are represented by the top and bottom of the graph), given the input of all the values that need to be displayed.
A function that, given the scale, returns a physical Y coordinate for a given chart value (e.g. "20" should be displayed 95 pixels above the bottom of the graph)
A Grid or other Panel to lay out separate areas for the title, legend, x axis, y axis (or axes, because you could potentially support more than one) and of course the plot area.
A function that determines the actual physical size of the plot area so you can physically scale (not the same as the scale mentioned earlier) the plot to fit inside the control. Don't forget that the size of the axes and other surrounding elements will have to be considered- even though they might not be drawn yet.
A method (or separate methods, which is how I did it) to draw each of axes and the lines. I used a Canvas, TextBlocks, PolyLines and Polygons (for markers).
(If you want to be professional) a method for calculating the overlap between marker labels and moving them apart so they're readable.
I need Anti-alias edge of my turned rectangle more than normally.
My code is like this:
<Rectangle Margin="20,20,147,135" Fill="#FFCAD2DE" RenderOptions.EdgeMode="Unspecified">
<Rectangle.Effect>
<DropShadowEffect BlurRadius="4" ShadowDepth="2" Opacity=".5"/>
</Rectangle.Effect>
<Rectangle.RenderTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="6" />
</Rectangle.RenderTransform>
</Rectangle>
I change the angle slowly programmatically...
But the result is aliased in some angles like below image (left side). I want edge of my rectangle be fully smooth in all angles like the below image (right side).
EDIT1:
I use .NET 3.5
The following steps can help
A)
First i moved my project from .NET 3.5 to .Net 4.5 without any changes in it and the result was:
It looks like very smoother
B)
Layout Rounding:
What is Layout Rounding and how to use it in WPF 4
When an object edge falls in the middle of a pixel device, the
DPI-independent graphics system can create rendering artifacts, such
as blurry or semi-transparent edges.
Previous versions of WPF included
pixel snapping to help handle this case. Silverlight 2 introduced
layout rounding, which is another way to move elements so that edges
fall on whole pixel boundaries.
WPF now supports layout rounding with
the UseLayoutRounding attached property on FrameworkElement. Drawing
objects on pixel boundaries eliminates the semi-transparent edges that
are produced by anti-aliasing, when an edge falls in the middle of a
device pixel. When you use layout rounding, the layout system creates
small variations in the column or row measurements to avoid sub-pixel
rendering.
The following code uses UseLayoutRounding attached property set on a single pixel-width line. You can see the difference that layout rounding makes when you resize the window slowly.
<StackPanel Width="150" Margin="7" Orientation="Horizontal">
<!-- Single pixel line with layout rounding turned OFF.-->
<Rectangle UseLayoutRounding="False" Width="45.6" Margin="10" Height="1" Fill="Red"/>
<!-- Single pixel line with layout rounding turned ON.-->
<Rectangle UseLayoutRounding="True" Width="45.6" Margin="10" Height="1" Fill="Red"/>
</StackPanel>
C)
SnapsToDevicePixels
Note
You should set UseLayoutRounding to true on the root
element. The layout system adds child coordinates to the parent
coordinates; therefore, if the parent coordinates are not on a pixel
boundary, the child coordinates are also not on a pixel boundary.
If UseLayoutRounding cannot be set at the root, set
SnapsToDevicePixels on the child to obtain the effect that you want.
It looks as if this isn't possible: RotateTranform causes alias edges on border control or a Rectangle.
From there:
Fortunately, the edge becomes perfectly soomth for 25+ degree rotations on my computer. If you eventually find a way to solve your problem, please share with us. It will be very beneficial for other community members having the similar questions.
I have a bit of a problem with XAML and Blend. We;ve converted the company logo from AI -> SVG -> XAML, cleaned it up a bit and its has produced a very peculiar source. In essence it is applying a "flip" transformation to a collection of paths:
<Canvas Width="640" Height="200" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="640" Height="200" RenderTransform="1,0,0,-1,0,200" >
<!-- L -->
<Canvas RenderTransform="1,0,0,1,317.0645,64.3652">
<Path>
<Path.Data>
<PathGeometry FillRule="Nonzero" Figures="M0,0L0,75.065 18.743,76.83 18.743,17.066 43.553,17.066 38.564,0 0,0z" />
</Path.Data>
</Path>
</Canvas>
... etc
Note the RenderTransform="1,0,0,-1,0,200" node in the enclosing Canvas. It flips the canvas upside down and lowers it 200 down to adjust to root canvas position. Given taht Logo looks ok when rendered means that all graphic elements in the xaml are actually upside down :). Maybe its an AI thing, I don't know.
The problem is that I now use Blend to convert this canvas into a GraphicBrush. The blend omits the transform and the logo looks upiside down when brush is applied. Rather then again transforming at each brush recipient I was thinking that maybe we should just fix it at the root, e.g. convert all paths so that they render properly to begin with avoiding a need for root transform.
Question: is there a programmatic (or any other way) to do this? Meaning, can I render this canvas and then serialize a rendered (after all transforms applied) DOM into an XAML?
Or to make the question clearer: there are two transforms applied to each path (as seen above, one global flip and one local shift for each path), can I somehow apply those transforms to each path/its points so that paths render without them.
You can use the Export to XAML feature built in to Expression Design in the Microsoft Expression suite.
Or personally I normally use Mike Swanson's AI to XAML converter since I'm normally already working in Illustrator for more complex vector graphics.
Both would provide a better result than the converter you used. Not sure where that original conversion got so wacky with the RenderTransforms etc but as you've found, it helps to have a good conversion to start with then going through the trouble of fixing a bad one haha.
Cheers!
I would like to make a sector of a circle on WP7. I tried to do this with the ellipse class and I found a lot of solution, which make a gauge or pie chart or something, but I need just the essence.
Could anyone help?
the aim is to show just one part of a circle (or ellipse). Like the yellow area in the picture:
Thanks,
Laci
Here's a fairly simple solution to the problem, though it does not use an Ellipse and it requires a little trigonometry:
<Path Fill="Black"
Data="M0,0 L0,-100 A100,100 0 0 1 70.7,-70.7 z" />
The Data property uses Path Markup Syntax.
The "M" at the beginning tells the pen to Move to the location 0,0.
The "L" tells the pen to draw a Line from the current location (0, 0) to 0,-100.
The "A" tells the pen to draw an elliptical Arc from the current location to 70.7,-70.7 (the "100,100" portion determines the horizontal and vertical radius of the ellipse and the "0 0 1" portion is for RotationAngle, IsLargeArc, and SweepDirection (1 for clockwise, 0 for counter-clockwise)).
The "z" tells the pen to close or complete the shape (which will cause a line to be drawn from 70.7,-70.7 back to 0,0).
Where did the 70.7 come from? Well, this particular arc sweeps out an angle of 45 degrees from a circle with radius 100, so the coordinates 70.7,-70.7 are determined by 100 * sin(45) and 100 * cos(45).
You need to do something like this:
define a canvas wrapper for ellipse
define the visible part of the canvas (clip). For this part you need to use PathGeometry as the Clip to define the slice of the circle you want to be visible. (see link)
<Canvas>
<Canvas.Clip>
<PathGeometry>
// define your path here (see link above)
</PathGeometry>
<Ellipse Background="Yellow" Width="200" Height="200" />
</Canvas.Clip>
</Canvas>
Alternatively you can use CombinedGeometry to combine a PathGeometry and EllipseGeometry to form the slice. (the link provides a good example of CombinedGeometry)
I want to seamlessly tile a bunch of different-colored Rectangles in WPF. That is, I want to put a bunch of rectangles edge-to-edge, and not have gaps between them.
If everything is aligned to pixels, this works fine. But I also want to support arbitrary zoom, and ideally, I don't want to use SnapsToDevicePixels (because it would compromise quality when the image is zoomed way out). But that means my Rectangles sometimes render with gaps. For example:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black">
<Canvas SnapsToDevicePixels="False">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</Canvas.RenderTransform>
<Rectangle Canvas.Left="25" Width="100" Height="100" Fill="#CFC"/>
<Rectangle Canvas.Left="125" Width="100" Height="100" Fill="#CCF"/>
</Canvas>
</Page>
If the ScaleTransform's ScaleX is 1, then the Rectangles fit together seamlessly. When it's 0.5, there's a dark gray streak between them. I understand why -- the combined semi-transparent edge pixels don't combine to be 100% opaque. But I would like a way to fix it.
I could always just make the Rectangles overlap, but I won't always know in advance what patterns they'll be in (this is for a game that will eventually support a map editor). Besides, this would cause artifacts around the overlap area when things were zoomed way in (unless I did bevel-cut angles on the underlapping portion, which is an awful lot of work, and still causes problems at corners).
Is there some way I can combine these Rectangles into a single combined "shape" that does render without internal gaps? I've played around with GeometryDrawing, which does exactly that, but then I don't see a way to paint each RectangleGeometry with a different-colored brush.
Are there any other ways to get shapes to tile seamlessly under an arbitrary transform, without resorting to SnapsToDevicePixels?
You might consider using guidelines (see GuidelineSet on MSDN) and overriding the Rectangles' OnRender methods so that their boundaries line up with the pixel boundaries of the device. WPF uses guidelines to determine whether and where to snap drawings.
Internally, it's exactly what SnapsToDevicePixels is using to ensure that objects line up with the device's pixels, but by placing guidelines manually you'll be able to control when the snapping behaviour is applied and when it is not (so when your image is zoomed all of the way out, you can avoid drawing guidelines, or only draw guidelines where your shapes lie next to other shapes, and rely on WPF's anti-aliasing to take care of the rest). You might be able to do it with an attached property so that you can apply it to any element, though if it's only one type of element (e.g. Rectangle) that you need this behaviour on, it's probably not worth the extra effort.
It seems like Microsoft is aware of this problem, too - WPF 4.0 is expected to feature Layout Rounding, which, like the version in Silverlight, rounds off non-integer values at the Render pass when layout rounding has been enabled.
I guess the gaps are not actual gaps but the stroke that is painted. When you scale it down than you just make the stroke smaller to a point where it is not visible anymore. I tried to paint the stroke in the color of the rectangle wich works just fine on any scale.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black">
<Canvas SnapsToDevicePixels="False">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</Canvas.RenderTransform>
<Rectangle Canvas.Left="25" Width="100" Height="100" Fill="#CFC" Stroke="#CFC"/>
<Rectangle Canvas.Left="125" Width="100" Height="100" Fill="#CCF" Stroke="#CCF"/>
</Canvas>
</Page>