How can we animate an arc segment in WPF. Below is the code attached.
PathGeometry pg = new PathGeometry();
PathFigure pf = new PathFigure();
LineSegment ls1 = new LineSegment();
LineSegment ls2 = new LineSegment();
ArcSegment arc = new ArcSegment();
double xc = canvas.Width / 2 - 50 + dx;
double yc = canvas.Height / 2 + dy;
double r = 0.7 * xc; // To control the Explosion generally for all the slices
pf.IsClosed = true;
pf.StartPoint = new Point(xc, yc);
pf.Segments.Add(ls1);
pf.Segments.Add(arc);
pf.Segments.Add(ls2);
pg.Figures.Add(pf);
path.Data = pg;
arc.IsLargeArc = isLargArc;
ls1.Point = new Point(xc + r * Math.Cos(startAngle), yc + r * Math.Sin(startAngle));
arc.SweepDirection = SweepDirection.Clockwise;
arc.Point = new Point(xc + r * Math.Cos(endAngle), yc + r * Math.Sin(endAngle));
arc.Size = new Size(r, r);
ls2.Point = new Point(xc + r * Math.Cos(endAngle), yc + r * Math.Sin(endAngle));
Duration duration = new Duration(TimeSpan.FromSeconds(5));
pau = new PointAnimationUsingPath();
pau.PathGeometry = pg;
pau.FillBehavior = FillBehavior.Stop;
pau.Duration = new Duration(TimeSpan.FromSeconds(5));
I am trying to get the arc segment to animate as in figure from one position to another. There are a list of arc segments which animate one after the other.
You can use Storyboard class to animate. You can for example animate position and RenderTransform it should work
Related
I'm trying to get an image and apply a drop shadow to it and save back as an image.
So far, the only way to do that, while not using third party solutions is to use the DropShadowEffect in a DrawingVisual:
var drawingVisual = new DrawingVisual();
drawingVisual.Effect = new DropShadowEffect
{
Color = Color.FromArgb(255, 0, 0, 0),
BlurRadius = 5,
Opacity = 1,
Direction = 45,
ShadowDepth = 6
};
using (var drawingContext = drawingVisual.RenderOpen())
{
var left = 0; //??
var top = 0; //??
var totalWidth = left + image.Width; //??
var totalHeight = top + image.Height; //??
//Background.
drawingContext.DrawRectangle(new SolidColorBrush(Colors.White), null, new Rect(0,0, totalWidth, totalHeight));
//Image.
drawingContext.DrawImage(image, new Rect(left, top, image.Width, image.Height));
}
var frameHeight = image.PixelHeight; //??
var frameWidth = image.PixelWidth; //??
//Converts the Visual (DrawingVisual) into a BitmapSource.
var bmp = new RenderTargetBitmap(frameWidth, frameHeight, imageDpi, imageDpi, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
//Creates a PngBitmapEncoder and adds the BitmapSource to the frames of the encoder.
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
//Saves the image into a file using the encoder.
using (Stream stream = File.Create(frame.Path))
encoder.Save(stream);
I have no idea the math required to detect the exact pixel offsets at all sides for a given DropShadowEffect.
Is there any built in way to measure it or should I do it manually?
How can be done manually?
If you take a look at the .NET source code for the DropShadowEffect there is an internal method GetRenderBounds that you can use to formulate the answer you're looking for.
While GetRenderBounds is not available for you to use, the code is simple enough that you can create your own helper method.
Here's the code for reference:
/// <summary>
/// Takes in content bounds, and returns the bounds of the rendered
/// output of that content after the Effect is applied.
/// </summary>
internal override Rect GetRenderBounds(Rect contentBounds)
{
Point topLeft = new Point();
Point bottomRight = new Point();
double radius = BlurRadius;
topLeft.X = contentBounds.TopLeft.X - radius;
topLeft.Y = contentBounds.TopLeft.Y - radius;
bottomRight.X = contentBounds.BottomRight.X + radius;
bottomRight.Y = contentBounds.BottomRight.Y + radius;
double depth = ShadowDepth;
double direction = Math.PI/180 * Direction;
double offsetX = depth * Math.Cos(direction);
double offsetY = depth * Math.Sin(direction);
// If the shadow is horizontally aligned or to the right of the original element...
if (offsetX >= 0.0f)
{
bottomRight.X += offsetX;
}
// If the shadow is to the left of the original element...
else
{
topLeft.X += offsetX;
}
// If the shadow is above the original element...
if (offsetY >= 0.0f)
{
topLeft.Y -= offsetY;
}
// If the shadow is below the original element...
else
{
bottomRight.Y -= offsetY;
}
return new Rect(topLeft, bottomRight);
}
I hope this helps.
Update from the author of the question
Here's the actual code that I'm using:
//Draws image with shadow.
using (var drawingContext = drawingVisual.RenderOpen())
{
//Measure drop shadow space.
var point1 = new Point(0 - model.BlurRadius / 2d, 0 - model.BlurRadius / 2d);
var point2 = new Point(image.PixelWidth + model.BlurRadius / 2d, image.PixelHeight + model.BlurRadius / 2d);
var num1 = Math.PI / 180.0 * model.Direction;
var num2 = model.Depth * Math.Cos(num1);
var num3 = model.Depth * Math.Sin(num1);
if (num2 >= 0.0)
point2.X += num2; //If the shadow is horizontally aligned or to the right of the original element...
else
point1.X += num2; //If the shadow is to the left of the original element...
if (num3 >= 0.0)
point1.Y -= num3; //If the shadow is above the original element...
else
point2.Y -= num3; //If the shadow is below the original element...
var left = Math.Abs(point1.X);
var top = Math.Abs(point1.Y);
var totalWidth = left + point2.X;
var totalHeight = top + point2.Y;
//Image.
drawingContext.DrawImage(image, new Rect((int)left, (int)top, image.PixelWidth, image.PixelHeight));
frameHeight = (int)totalHeight;
frameWidth = (int)totalWidth;
}
Please see this picture:
I need to move by mouse one of the two points along an arc of 90°
Please have in mind that I can rotate the Arrow and the whole thing will rotate as well.
What I did:
public override bool HandleMouseMove(MouseEventArgs e)
{
double angleRad = 0;
double new_cx = 0;
double new_cY = 0;
double currentAngle = Arrow.Rotation.Angle;
Point pt = TMEMControl.ScreenToWorld(new Point(e.X, e.Y));
Point center = TMEMControl.ScreenToWorld(new Point(Arrow.Translation.X, Arrow.Translation.Y));
Point c = new Point(center.X + 25, center.Y);
angleRad = (-currentAngle) * (Math.PI / 180);
new_cx = center.X + Math.Cos(angleRad) * 25;
new_cY = center.Y + Math.Sin(angleRad) * 25;
c = new Point(new_cx, new_cY);
Vector CenterPt = new Vector((pt.X-center.X),(pt.Y-center.Y));
Vector CenterC = new Vector((c.X - center.X),(c.Y - center.Y));
fieldOfView = Vector.AngleBetween(CenterPt, CenterC);
if (fieldOfView >= 90) fieldOfView = 90;
if (fieldOfView <= 0) fieldOfView = 0;
TMEMControl.Settings.VideoOverlayHelper.FieldOfView = (float)SmartFrame.Basic.BaseAngle.Deg2Rad(fieldOfView*2);
updateFieldofView = (float)fieldOfView;
if (updateFieldofView > 87) updateFieldofView = 87;
if (updateFieldofView < 0) updateFieldofView = 0;
return true;
}
public void Reset(int id, double x, double y, double angle)
{
double angleInRadians = 0;
Point center = new Point(x + 15, y + 20);
Point refP = new Point(x + 15, (y - 80));
distance = Point.Subtract(refP, center).Length;
double new_x = 0;
double new_y = 0;
ControlPoint cpi = CameraCtrl.getCP(id);
if (null != cpi)
{
switch (id)
{
case 0:
cpi.point = new Point(center.X, center.Y);
cpi.state = ControlPoint.States.Selectable;
break;
case 1:
cpi.point = new Point(x + 15, (y + 80));
angleInRadians = (float)SmartFrame.Basic.BaseAngle.Deg2Rad((angle + (87 - updateFieldofView)));
new_x = center.X + Math.Cos(-angleInRadians) * distance;
new_y = center.Y + Math.Sin(-angleInRadians) * distance;
cpi.point = new Point(new_x, new_y);
cpi.state = ControlPoint.States.Fixed;
break;
case 2:
cpi.point = new Point(x + 15, (y - 80));
angleInRadians = (float)SmartFrame.Basic.BaseAngle.Deg2Rad((angle + (180 - (87 - updateFieldofView))));
new_x = center.X + Math.Cos(-angleInRadians) * distance;
new_y = center.Y + Math.Sin(-angleInRadians) * distance;
cpi.point = new Point(new_x, new_y);
cpi.state = ControlPoint.States.Selectable;
break;
}
}
}
It's working 90% the problem is that once I snap/click on one of the red dots and move the mouse, the red dot is always few degrees ahead from the the mouse cursor. and I need to move it with the cursor Position.
And just for the info why I set always the angle to max 87°, is because I want to keep a little distance between the Arrow and my two lines.
I want to draw ellipse excluding cross inside of it. I have a suspision that I need to use opacity mask. Here is how I am trying to do it.
Color grey = Color.FromArgb(128, Colors.Gray.R, Colors.Gray.G, Colors.Gray.B);
double radius = Math.Min(ActualWidth, ActualHeight) / 2;
Brush ellipse_brush = new SolidColorBrush(grey);
CombinedGeometry cg = new CombinedGeometry();
Drawing maskDrawing = new GeometryDrawing(Brushes.Lime, null, cg);
DrawingBrush mask = new DrawingBrush(maskDrawing);
dc.PushOpacityMask(mask);
dc.DrawEllipse(ellipse_brush, new Pen(ellipse_brush, 0), new Point(radius, radius), radius, radius);
dc.Pop();
Thing is that I don't understand how to create CombinedGeometry for ellipse and two lines. Or maybe I am on the wrong path?
You do not need an opacity mask in conjunction with a CombinedGeometry.
Create the cross outline geometry from a GeometryGroup with two lines and an appropriate Pen, then combine it Xor with an EllipseGeometry and draw the result:
var radius = Math.Min(ActualWidth, ActualHeight) / 2;
var crossSize = 0.8 * radius;
var crossThickness = 0.3 * radius;
var centerPoint = new Point(radius, radius);
var ellipseGeometry = new EllipseGeometry(centerPoint, radius, radius);
var crossGeometry = new GeometryGroup();
crossGeometry.Children.Add(new LineGeometry(
new Point(centerPoint.X - crossSize / 2, centerPoint.Y - crossSize / 2),
new Point(centerPoint.X + crossSize / 2, centerPoint.Y + crossSize / 2)));
crossGeometry.Children.Add(new LineGeometry(
new Point(centerPoint.X - crossSize / 2, centerPoint.Y + crossSize / 2),
new Point(centerPoint.X + crossSize / 2, centerPoint.Y - crossSize / 2)));
var crossPen = new Pen
{
Thickness = crossThickness,
StartLineCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round
};
var crossOutlineGeometry = crossGeometry.GetWidenedPathGeometry(crossPen);
var combinedGeometry = new CombinedGeometry(GeometryCombineMode.Xor,
ellipseGeometry, crossOutlineGeometry);
dc.DrawGeometry(Brushes.Gray, null, combinedGeometry);
I built a system that rotates and scales objects using the most left and vertical center point of the object as the origin. After transforming, you can send the objects html/css3 information to the server and c# will attempt to redraw the scene that you created. C# is rotating the objects/images at the same degree but is rotating them from the vertical and horizontal center points this causes the object/images to change dimensions. I already have those changes calculated however there is an offset occurring with the x,y coordinates of the top left of the object/images
This is the method I've been attempting to work out to deal with the offsets:
int[] rotResult = new int[2];
int[] tLCoord = new int[2];
int[] tRCoord = new int[2];
int[] bLCoord = new int[2];
int[] bRCoord = new int[2];
int[] tLCoordTmp = new int[2];
int[] tRCoordTmp = new int[2];
int[] bLCoordTmp = new int[2];
int[] bRCoordTmp = new int[2];
float sin = (float)Math.Sin(angle * Math.PI / 180.0);
float cos = (float)Math.Cos(angle * Math.PI / 180.0);
tLCoord[0] = (originalX - Math.Abs(xMin));
tLCoord[1] = (originalY - Math.Abs(yMin));
tRCoord[0] = origwidth;
tRCoord[1] = 0;
bLCoord[0] = 0;
bLCoord[1] = (origheight * -1);
bRCoord[0] = origwidth;
bRCoord[1] = (origheight * -1);
tLCoordTmp[0] = Convert.ToInt32((tLCoord[0] * cos) - (tLCoord[1] * sin));
tLCoordTmp[1] = Convert.ToInt32((tLCoord[1] * cos) + (tLCoord[0] * sin));
tRCoordTmp[0] = Convert.ToInt32(((tLCoordTmp[0] + tRCoord[0]) * cos) - (tRCoord[1] * sin));
tRCoordTmp[1] = Convert.ToInt32(((tLCoordTmp[1] + tRCoord[1]) * cos) + (tRCoord[0] * sin));
bLCoordTmp[0] = Convert.ToInt32(((tLCoordTmp[0] + bLCoord[0]) * cos) - (bLCoord[1] * sin));
bLCoordTmp[1] = Convert.ToInt32(((tLCoordTmp[1] + bLCoord[1]) * cos) + (bLCoord[0] * sin));
bRCoordTmp[0] = Convert.ToInt32(((tLCoordTmp[0] + bRCoord[0]) * cos) - (bRCoord[1] * sin));
bRCoordTmp[1] = Convert.ToInt32(((tLCoordTmp[1] + bRCoord[1]) * cos) + (bRCoord[0] * sin));
if (angle >= 270)
{
rotResult[0] = tLCoordTmp[0];
rotResult[1] = tRCoordTmp[1];
}
else if (angle <= 90)
{
rotResult[0] = bLCoordTmp[0];
rotResult[1] = tLCoordTmp[1];
}
else if (angle > 90 && angle <= 180)
{
rotResult[0] = bRCoordTmp[0];
rotResult[1] = bLCoordTmp[1];
}
else if (angle > 180 && angle < 270)
{
rotResult[0] = tRCoordTmp[0];
rotResult[1] = bRCoordTmp[1];
}
return rotResult;
Immediately I know there are a few issues dealing with the way this formula works out in regards to coordinate planes and the way that c# and html/css both render visual elements, I've been running small experiments to offset against those and nothing seems to be getting any closer to a solution, any ideas?
The answer was to first calculate the image X,Y offsets from the top left corner of the object while rotating the image in c# via TranslateTransform and RotateTransform to prevent clipping, then independently calculate the proper X,Y coordinates for the object using the left most center point of the object after that it was only a matter of making a few conditional statements to deal with the quadrant based calculation differences
I'm creating a silverlight application where I have to dynamically create buttons. But I need to place them in a circle around the button that I click to generate the other buttons (picture here, the buttons should go on the black line surrounding the 'test project' button)
I don't know how many buttons will be generated each time but I do know the size of each button is static. I'm not quite sure how to do this. Currently my button creation is as follows
foreach (Item a in itemList)
{
Button newButton = new Button();
newButton.Height = 50;
newButton.Width = 50;
newButton.Content = a.getName();
newButton.Click += new RoutedEventHandler(addedClick);
newButton.HorizontalAlignment = HorizontalAlignment.Left;
newButton.VerticalAlignment = VerticalAlignment.Top;
newButton.Margin = new Thickness(0, 0, 0, 0);
newButton.Style = (Style)Application.Current.Resources["RB"];
buttons.Add(newButton);
}
My biggest problem is that I'm not quite sure how to get the center point of the 'test project' button.
EDIT: Okay, now that I have a set of coordinates for each button, how exactly do I go about placing them? I'm not sure how to use a canvas. I tried to set one up but it keeps acting like a stackpanel (no .setLeft/.setTop).
You mean something like the circle equation:
Double distanceFromCenter = 5;
Double angleInDegrees = 90;
Double angleAsRadians = (angleInDegrees* Math.PI) / 180.0;
Double centerX = 100;
Double centerY = 100;
Double x = centerX + Math.Cos(angleAsRadians) * distanceFromCenter;
Double y = centerY + Math.Sin(angleAsRadians) * distanceFromCenter;
that should give you a point that is distanceFromCenter units away from (centerX, center), at an angle of 90-degrees. Note this only works with radians so we have to convert to radians.
var radius = 100;
var angle = 360 / itmeList.Count * Math.PI / 180.0f;
var center = new Point(100, 100);
for (int i = 0; i < itemList.Count; i++)
{
var x = center.X + Math.Cos(angle * i) * radius;
var y = center.Y + Math.Sin(angle * i) * radius;
Button newButton = new Button();
newButton.RenderTransformOrigin = new Point(x, y);
newButton.Height = 50;
newButton.Width = 50;
newButton.Content = a.getName();
newButton.Click += new RoutedEventHandler(addedClick);
newButton.HorizontalAlignment = HorizontalAlignment.Left;
newButton.VerticalAlignment = VerticalAlignment.Top;
newButton.Margin = new Thickness(0, 0, 0, 0);
newButton.Style = (Style)Application.Current.Resources["RB"];
buttons.Add(newButton);
}
Assuming you want your buttons evenly spaced on the circle, you should first generate the list of angles you want them at. E.g.
double eachSection = 2 * Math.PI / count;
var anglesInRadians = Enumerable.Range(0, count).Select(x => x * eachSection);
Then use this formula to find the x/y coordinates of each angle, and use a Canvas or something to position the buttons in those positions
public static Point PointOnCircle(double radius, double angleInRadians, Point origin)
{
double x = radius * Math.Cos(angleInRadians) + origin.X;
double y = radius * Math.Sin(angleInRadians) + origin.Y;
return new Point(x, y);
}