(SVG or WPF XAML - I'm open to (and need) both. I don't suppose they are any different in implementation. Below example is in SVG).
I'm trying to find a way to use rotation on a linear gradient (in this case 270°), but then when the shape it fills is rotated, keep the linear gradient unchanged, as if the shape it is filling isn't rotated.
By way of example, this is what Microsoft Word can do. When filling an AutoShape with a linear gradient, you can uncheck "Rotate with shape" and then the linear gradient will stay in place no matter how you rotate the shape.
So, here's an example, I want to rotated the linear gradient by 270° on it's center (in the linearGradient definition, gradientTransform="rotate(270 0.5 0.5)"). After that, I want to rotate the path it fills 45° (in the path, transform="rotate(45 0.5 0.5)").
<svg width="960" height="540" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<svg x="265.24353" y="133.125046" width="337.287231" height="211.206543" viewBox="0 0 337.287231 211.206543" style="overflow: visible;" >
<defs>
<linearGradient id="linear" gradientTransform="rotate(270 0.5 0.5)" spreadMethod="pad" >
<stop offset="0%" stop-color="#FF0000"/>
<stop offset="48%" stop-color="#FF0000"/>
<stop offset="48%" stop-color="#0070C0"/>
<stop offset="100%" stop-color="#0070C0"/>
</linearGradient>
</defs>
<path id="9" fill="url(#linear)" transform="rotate(45 0.5 0.5)" stroke="#000000" stroke-width="5" stroke-linecap="round" d="M0,0L205.8147,0L205.8147,174.9073L0,174.9073Z"/>
</svg>
</svg>
Okay, so this doesn't work, right? It's not supposed to. So I try subtracting 45° (path's rotation) from 270° (lin grad's rotation) for 225° and then in the linearGradient set gradientTransform="rotate(225 0.5 0.5)". That gets me close. Really close. So then I try lowering the angle and voilà, it's about 221° that seems to be correct. 4° lower than 270°-45°.
So I try this strategy again with a path rotation 65° - I know the linearGradient's rotation (originally at 270°) won't be 205°, but somewhere close to that, ah ha! It's 201° (or some floating point of that) 4° lower than 270°-65°.
How do I know it's 4° lower than 270°-65° lower than the subtraction in both cases? I eyeball the divider in the linearGradient to ensure it is horizontal.
So would there be a way to calculate the above regardless of linearGradient rotation angle or the shape's rotation angle? For example, if the liner gradient's rotation is 180° (which divides the above example vertically) or any other degree? If I could figure how to insert JS Fiddle here, I'd put more examples that can be run.
UPDATE (on WPF):
#Clemens provided some keen insight into how this can be accomplished in WPF by transforming the PathGeometry, not the Path. Awesome.
Here's the sample of transforming the Path, which is identical the SVG above.
<Path RenderTransformOrigin="0.5,0.5" Data="M0,0L205.8147,0L205.8147,174.9073L0,174.9073Z" Stroke="Black" StrokeLineJoin="Miter" StrokeThickness="5">
<Path.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45" CenterY="0.5" CenterX="0.5"/>
</TransformGroup>
</Path.RenderTransform>
<Path.Fill>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,1" >
<LinearGradientBrush.RelativeTransform>
<TransformGroup>
<RotateTransform Angle="221" CenterY="0.5" CenterX="0.5"/>
</TransformGroup>
</LinearGradientBrush.RelativeTransform>
<GradientStopCollection>
<GradientStop Offset="0" Color="#FF0000" />
<GradientStop Offset="0.48" Color="#FF0000" />
<GradientStop Offset="0.48" Color="#0070C0" />
<GradientStop Offset="1" Color="#0070C0" />
</GradientStopCollection>
</LinearGradientBrush>
</Path.Fill>
</Path>
And here's the way to transform just the PathGeometry - it works exactly as I need (well, except for the center of rotation, but I think I can resolve that).
<Path RenderTransformOrigin="0.5,0.5" Stroke="Black" StrokeLineJoin="Miter" StrokeThickness="5" Canvas.Left="15" Canvas.Top="23" >
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="0,0" IsClosed="True">
<LineSegment Point="205.8147,0"/>
<LineSegment Point="205.8147,174.9073"/>
<LineSegment Point="0,174.9073"/>
</PathFigure>
</PathGeometry.Figures>
<PathGeometry.Transform>
<RotateTransform Angle="45" CenterY="0.5" CenterX="0.5"/>
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
<Path.Fill>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,1" >
<LinearGradientBrush.RelativeTransform>
<TransformGroup>
<RotateTransform Angle="270" CenterY="0.5" CenterX="0.5"/>
</TransformGroup>
</LinearGradientBrush.RelativeTransform>
<GradientStopCollection>
<GradientStop Offset="0" Color="#FF0000" />
<GradientStop Offset="0.48" Color="#FF0000" />
<GradientStop Offset="0.48" Color="#0070C0" />
<GradientStop Offset="1" Color="#0070C0" />
</GradientStopCollection>
</LinearGradientBrush>
</Path.Fill>
</Path>
SVG only:
The angles don't add up because of the way gradients are applied on an element. By default..
(...) It essentially scales the gradient to the size of your object, so you only have to specify coordinates in values from zero to one, and they're scaled to the size of your object automatically for you.
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Gradients
Another way to look at it; the gradient is first applied to a square, and then stretched to fit your element's bounding box. See this example where all three elements have the same 45 degrees gradient:
<svg width="960" height="540" xmlns="http://www.w3.org/2000/svg" >
<defs>
<linearGradient id="linear" gradientTransform="rotate(45 0.5 0.5)" spreadMethod="pad">
<stop offset="49%" stop-color="gold"/>
<stop offset="51%" stop-color="blue"/>
</linearGradient>
</defs>
<path fill="url(#linear)" d="M0,0 h170 v100 h-170"/>
<path fill="url(#linear)" d="M210,0 h100 v100 h-100"/>
<path fill="url(#linear)" d="M350,0 h100 v170 h-100"/>
</svg>
So in your case, where you want the gradient at a 225 degree angle, imagine squeezing the gradient back into a square form, and calculate what the angle looks like then.
Long story short, here is some code:
https://jsfiddle.net/bc4k4Lcv/
Related
I'm making an app that is a venn diagram and I don't know how to do the first step. There are to ellipses and I want to color the shared area of two ellipses.
The green area is the shared area I meant.
You can use intersect mode on a combinedgeometry to find the parts overlap:
<Grid>
<Grid.Background>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<Canvas>
<Path Fill="Yellow">
<Path.Data>
<EllipseGeometry Center="150,50" RadiusX="75" RadiusY="75" />
</Path.Data>
</Path>
<Path Fill="Yellow">
<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="75" RadiusY="75" />
</Path.Data>
</Path>
<Path Fill="Green">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Intersect">
<CombinedGeometry.Geometry1>
<EllipseGeometry Center="150,50" RadiusX="75" RadiusY="75"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry Center="50,50" RadiusX="75" RadiusY="75"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
Position textblocks in the grid on top of the background shapes using rows and columns.
Or just put everything in a canvas rather than using a visualbrush.
Use Canvas.Left and Canvas.Top to position some textblocks on top of the ellipses.
i want to create a simple cross, that consists of two lines. The lines should have different colors. I've created a class that inherits form Shape. This class contains the two lines and computes the coordinates of the lines. I've read that i have to implement the DefiningGeometry property if i inherit from Shape. But how can i return both lines in the get section of that property?
Thanks in advance.
It sounds like you could use the CombinedGeometry Class to combine your lines together... the only thing is that you'll need to use LineGeometry classes instead of Lines. You could do something like this (from the linked CombinedGeometry page on MSDN):
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the XOR combine mode. -->
<CombinedGeometry GeometryCombineMode="Xor">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Of course, you'd want to replace these EllipseGeometry objects with LineGeometry objects, but that shouldn't be difficult as they have similar properties.
UPDATE >>>
Unfortunately, I don't think that you can use a CombinedGeometry object that contains geometries of different colours... the whole shape would have to be painted with one Brush. However, you could fake two colours with cleverly positioned GradientStops. Also, as #Clemens mentioned, perhaps a GeometryGroup would be easier for you to use... try something like this:
<Path StrokeThickness="5" Fill="Blue" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path.Data>
<GeometryGroup>
<LineGeometry StartPoint="50,0" EndPoint="50,100" />
<LineGeometry StartPoint="0,50" EndPoint="100,50" />
</GeometryGroup>
</Path.Data>
<Path.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="LightGreen" Offset="0" />
<GradientStop Color="LightGreen" Offset="0.475" />
<GradientStop Color="Red" Offset="0.475" />
<GradientStop Color="Red" Offset="0.525" />
<GradientStop Color="LightGreen" Offset="0.525" />
<GradientStop Color="LightGreen" Offset="0" />
</LinearGradientBrush>
</Path.Stroke>
</Path>
This Brush will appear as if it were actually different colours on the two lines:
Then all you'll need to do is to convert this into C# to return it from the DefiningGeometry property. Please use the examples from the linked pages and the GeometryGroup class page on MSDN to help you with this.
You may draw two differently colored lines by means of two GeometryDrawings in a DrawingBrush that fills a Rectangle:
<Rectangle Width="20" Height="20">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Geometry="M0,-10 L0,10">
<GeometryDrawing.Pen>
<Pen Brush="Blue" Thickness="3"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Geometry="M-10,0 L10,0">
<GeometryDrawing.Pen>
<Pen Brush="Red" Thickness="3"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
I have implement Callout control of Blend. Problem which I am facing is anchor point of the Callout starts with some margin from the top, While I want to have anchor from the top left of the callout.
Any help will be appreciated.
What I have now:
What I would like to have:
You seem to be mistaken about using this control. From MSDN, the Callout.AnchorPoint property Gets or sets the position of the callout relative to the top and left corner. It is used for positioning the control and does not alter the shape of the Callout.
UPDATE >>>
Dude!!! That's a really simple shape... just draw your own one with a Path... then you can make it any shape you want:
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<RectangleGeometry RadiusX="20" RadiusY="20" Rect="0,0,300,200">
<RectangleGeometry.Transform>
<TranslateTransform X="30" />
</RectangleGeometry.Transform>
</RectangleGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry>
<PathFigure StartPoint="0,30">
<LineSegment Point="50,10" />
<LineSegment Point="50,50" />
</PathFigure>
</PathGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
<Path.Effect>
<DropShadowEffect Color="Black" Opacity="0.4" Direction="-135"
ShadowDepth="10" />
</Path.Effect>
</Path>
What's the easiest way to draw an arrow at the end of a QuadraticBezierSegment? The tricky part is to get the correct rotation to mach the incoming line segment.
Any ideas on how to go about this? Should I extend PathSegment?
I've got this for drawing a simple bezier line.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="100,430">
<PathFigure.Segments>
<PathSegmentCollection>
<QuadraticBezierSegment Point1="150,250" Point2="250,300" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
You could define the geometry for the arrow head....but it would take trial and error to correctly orientate it on the end of the bezier curve.
Instead you could use this control and define the endcap you wanted using geometry and it properly places it on the end of the "line".
http://blogs.msdn.com/b/mrochon/archive/2011/01/10/custom-line-caps-in-wpf.aspx
<loc:CappedLine Stroke="Red" StrokeThickness="1" Canvas.Left="40" Canvas.Top="200" RenderTransformOrigin="0.5,0.5" Height="107" Width="195">
<loc:CappedLine.EndCap>
<GeometryGroup>
<LineGeometry StartPoint="0,0" EndPoint="10,10"/>
<LineGeometry StartPoint="0,0" EndPoint="10,-10"/>
</GeometryGroup>
</loc:CappedLine.EndCap>
<loc:CappedLine.LinePath>
<PathGeometry Figures="M0,0 C1,1 10.5,75.5 48.5,66.5 86.5,57.5 5,3.5000146 105.5,16.500091 157.5,29.500166 164.5,87.500505 164.5,87.500505" />
</loc:CappedLine.LinePath>
</loc:CappedLine>
<loc:CappedLine Stroke="Red" StrokeThickness="1" Canvas.Left="180" Canvas.Top="200" RenderTransformOrigin="0.5,0.5" Height="107" Width="195">
<loc:CappedLine.EndCap>
<GeometryGroup>
<LineGeometry StartPoint="0,0" EndPoint="10,10"/>
<LineGeometry StartPoint="0,0" EndPoint="10,-10"/>
</GeometryGroup>
</loc:CappedLine.EndCap>
<loc:CappedLine.LinePath>
<PathGeometry Figures="M0,0 C1,1 10.5,75.5 48.5,66.5 86.5,57.5 5,3.5000146 105.5,16.500091" />
</loc:CappedLine.LinePath>
</loc:CappedLine>
I encountered following problem during creation of radioactive geometry:
Orange dot represents RadialGradientBrush center point (0,0). It should be in the center of black circle, not on the bottom. This cause another problem: spinning animation.
Is there a way to fix this at the geometry level ? I could fix it by setting brush's center point explicitly, but that is inconvenient and brittle.
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<PathGeometry Figures="
M-10,0 Q0,5 10,0
Q20,25 25,50
Q0,60 -25,50
Q-20,25 -10,0 Z">
<PathGeometry.Transform>
<TranslateTransform Y="15" />
</PathGeometry.Transform>
</PathGeometry>
<PathGeometry Figures="
M-10,0 Q0,5 10,0
Q20,25 25,50
Q0,60 -25,50
Q-20,25 -10,0 Z" >
<PathGeometry.Transform>
<TransformGroup>
<TranslateTransform Y="15" />
<RotateTransform Angle="120" />
</TransformGroup>
</PathGeometry.Transform>
</PathGeometry>
<PathGeometry Figures="
M-10,0 Q0,5 10,0
Q20,25 25,50
Q0,60 -25,50
Q-20,25 -10,0 Z" >
<PathGeometry.Transform>
<TransformGroup>
<TranslateTransform Y="15" />
<RotateTransform Angle="240" />
</TransformGroup>
</PathGeometry.Transform>
</PathGeometry>
<EllipseGeometry RadiusX="10" RadiusY="10" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<RadialGradientBrush ColorInterpolationMode="ScRgbLinearInterpolation">
<GradientStop Offset="0" Color="Orange" />
<GradientStop Offset="0.03" Color="Black" />
</RadialGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Pen>
<Pen Thickness="0">
<Pen.Brush>
<SolidColorBrush Color="Black" />
</Pen.Brush>
</Pen>
</GeometryDrawing.Pen>
</GeometryDrawing>
This is caused by the fact that the top of the box cuts off the top of the big circle. It is cut off because the top two petals are rotated.
You could fix this by adding the outer circle (transparent) to the geometry or perhaps by adding a margin to the top.
this will move down the black parts of the drawing, centering the orange dot and fixing the rotation.
Does this make sense? It is hard to explain, when needed I could create a sketch.