I'm using DrawingContext.DrawText and DrawingContext.PushTransfrom to create rotated text on Visual Layer in WPF but as you see in the image below, the rotated text is rather blurry in some areas of the image..
Is there any option I can use to improve this? The Arial font is used for the text.
public class BeamTextDrawing : FrameworkElement
{
private readonly VisualCollection _visuals;
public BeamTextDrawing(double scale)
{
if (scale <= 0)
{
scale = 1;
}
var typeface = Settings.BeamTextTypeface;
var cultureinfo = Settings.CultureInfo;
var flowdirection = Settings.FlowDirection;
var textsize = Settings.BeamTextSize / scale;
var beamtextcolor = Settings.InPlanBeamTextColor;
_visuals = new VisualCollection(this);
foreach (var beam in Building.BeamsInTheElevation)
{
var drawingVisual = new DrawingVisual();
using (var dc = drawingVisual.RenderOpen())
{
var text = Convert.ToString(beam.Section.Id);
//text = scale.ToString();
var ft = new FormattedText(text, cultureinfo, flowdirection,
typeface, textsize, beamtextcolor)
{
TextAlignment = TextAlignment.Center
};
var x1 = beam.ConnectivityLine.I.X;
var y1 = beam.ConnectivityLine.I.Y;
var x2 = beam.ConnectivityLine.J.X;
var y2 = beam.ConnectivityLine.J.Y;
var v1 = new Point(x2, y2) - new Point(x1, y1);
var v2 = new Vector(1, 0);
var hwidth = textsize;
var l = Geometrics.GetOffset(x1, y1, x2, y2, hwidth + 5/scale);
var angle = Vector.AngleBetween(v1, v2);
var x = 0.5 * (l.X1 + l.X2);
var y = 0.5 * (l.Y1 + l.Y2);
var r = new RotateTransform(angle, x, SelectableModel.FlipYAxis(y));
dc.PushTransform(r);
dc.DrawText(ft, SelectableModel.FlipYAxis(x, y));
}
_visuals.Add(drawingVisual);
}
}
protected override Visual GetVisualChild(int index)
{
return _visuals[index];
}
protected override int VisualChildrenCount
{
get
{
return _visuals.Count;
}
}
}
Update:
Here is the image after using this code:
TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
I'm still getting blurry results. Look at the middle beam text at the lower part of the image.
You can change the TextFormattingMode to prevent blurry text:
public BeamTextDrawing(double scale)
{
TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
// .. Your other code
Related
In Revit API, I want to draw a ModelLine which can preview before second point picked.
How can I do that?
I resolved.
Try this below code:
public class Command : IExternalCommand
{
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Application app = uiapp.Application;
Document doc = uidoc.Document;
Selection sel1 = uidoc.Selection;
List<XYZ> tempXYZ = new List<XYZ>(1);
XYZ p1 = sel1.PickPoint();
XYZ p2 = null;
//tempXYZ.Add(p3);
ModelCurve visualLine = null;
using (TransactionGroup tGroup = new TransactionGroup(doc))
{
tGroup.Start();
Redraw:
using (Transaction t = new Transaction(doc))
{
t.Start("Step 1");
Line line = Line.CreateBound(p1, getP3(uidoc));
Plane geomPlane = Plane.CreateByNormalAndOrigin(doc.ActiveView.ViewDirection, doc.ActiveView.Origin);
SketchPlane sketch = SketchPlane.Create(doc, geomPlane);
visualLine = doc.Create.NewModelCurve(line, sketch) as ModelCurve;
doc.Regenerate();
uidoc.RefreshActiveView();
goto Redraw;
t.Commit();
}
tGroup.Commit();
}
return Result.Succeeded;
}
private XYZ getP3(UIDocument uidoc)
{
UIView uiview = GetActiveUiView(uidoc);
Rectangle rect = uiview.GetWindowRectangle();
System.Drawing.Point p = System.Windows.Forms.Cursor.Position;
System.Windows.Forms.Cursor.Position = new System.Drawing.Point(p.X, p.Y);
double dx = (double)(p.X - rect.Left) / (rect.Right - rect.Left);
double dy = (double)(p.Y - rect.Bottom) / (rect.Top - rect.Bottom);
IList<XYZ> corners = uiview.GetZoomCorners();
XYZ a = corners[0];
XYZ b = corners[1];
XYZ v = b - a;
XYZ p3 = a + dx * v.X * XYZ.BasisX + dy * v.Y * XYZ.BasisY;
return p3;
}
//Convert Document hiện hành thành UIView
private UIView GetActiveUiView(UIDocument uidoc)
{
Document doc = uidoc.Document;
View view = doc.ActiveView;
IList<UIView> uiviews = uidoc.GetOpenUIViews();
UIView uiview = null;
foreach (UIView uv in uiviews)
{
if (uv.ViewId.Equals(view.Id))
{
uiview = uv;
break;
}
}
return uiview;
}
}
I have created a Kinect app using WPF and Microsoft Kinect SDK v2.
I have successfully displayed an image on all joint points using the following code:
// Draw
if (joint.JointType == JointType.SpineShoulder)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(#"C:\Users\myimage.jpg", UriKind.Relative);
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
var img = new Image { Source = bitmap, Height = 50, Width = 50 };
Canvas.SetLeft(img, point.X - img.Width / 2);
Canvas.SetTop(img, point.Y - img.Height / 2);
canvas.Children.Add(img);}
Now instead of an image on each joint, I want to merge 3 joints (spine_shoulder - center joint , shoulder right , shoulder left) its my opinion and overlay an image onto them, so that image rotates according to how joint positions change.
I tried using the code explained in this article, with no luck...
lets assume if we overlay the image on top of any block how it show
how it will rotate https://www.youtube.com/watch?v=pAljofdcMw8
As suggested by #Vangos I tried like below
public partial class Window1 : Window
{
public static ObservableCollection<string> selectedImg = new ObservableCollection<string>();
KinectSensor _sensor;
MultiSourceFrameReader _reader;
IList<Body> _bodies;
private static string imagepath = #"C:\Users\demo.png";
CameraMode _mode = CameraMode.Color;
public Window1()
{
InitializeComponent();
imageItems.ItemsSource = Page1.folders;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_sensor = KinectSensor.GetDefault();
if (_sensor != null)
{
_sensor.Open();
_reader = _sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color | FrameSourceTypes.Depth | FrameSourceTypes.Infrared | FrameSourceTypes.Body);
_reader.MultiSourceFrameArrived += Reader_MultiSourceFrameArrived;
}
}
void Reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
var reference = e.FrameReference.AcquireFrame();
// Body
using (var frame = reference.BodyFrameReference.AcquireFrame())
{
if (frame != null)
{
canvas.Children.Clear();
_bodies = new Body[frame.BodyFrameSource.BodyCount];
frame.GetAndRefreshBodyData(_bodies);
foreach (var body in _bodies)
{
if (body.IsTracked)
{
// COORDINATE MAPPING
foreach (Joint joint in body.Joints.Values)
{
if (joint.TrackingState == TrackingState.Tracked)
{
// 3D space point
CameraSpacePoint jointPosition = joint.Position;
// 2D space point
Point point = new Point();
if (_mode == CameraMode.Color)
{
ColorSpacePoint colorPoint = _sensor.CoordinateMapper.MapCameraPointToColorSpace(jointPosition);
point.X = float.IsInfinity(colorPoint.X) ? 0 : colorPoint.X;
point.Y = float.IsInfinity(colorPoint.Y) ? 0 : colorPoint.Y;
}
else if (_mode == CameraMode.Depth || _mode == CameraMode.Infrared) // Change the Image and Canvas dimensions to 512x424
{
DepthSpacePoint depthPoint = _sensor.CoordinateMapper.MapCameraPointToDepthSpace(jointPosition.);
point.X = float.IsInfinity(depthPoint.X) ? 0 : depthPoint.X;
point.Y = float.IsInfinity(depthPoint.Y) ? 0 : depthPoint.Y;
}
//// Draw a images based on joint type
JointType _start = JointType.SpineShoulder;
JointType _center = JointType.ShoulderRight;
JointType _end = JointType.ShoulderLeft;
if (joint.JointType == JointType.SpineShoulder)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(imagepath, UriKind.Relative);
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
var img = new Image { Source = bitmap, Height = 50, Width = 50 };
//Add a RotateTransform
img.RenderTransformOrigin = new Point(0.5, 0.5);
double angle = Extension.Angle(body.Joints[_start], body.Joints[_center], body.Joints[_end]);
img.RenderTransform = new RotateTransform(angle);
Canvas.SetLeft(img, point.X - img.Width / 2);
Canvas.SetTop(img, point.Y - img.Height / 2);
canvas.Children.Add(img);
}
}
}
}
}
}
}
}
}
enum CameraMode
{
Color,
Depth,
Infrared
}
You can use the following algorithm:
1) Find the angle between the joints (code).
using LightBuzz.Vitruvius;
double angle = joint1.Angle(joint2, joint3);
2) Add a RotateTransform to the desired image and rotate the image according to the angle you calculated.
img.RenderTransformOrigin = new Point(0.5, 0.5);
img.RenderTransform = new RotateTransform(angle);
Your XAML code:
<Grid>
<Canvas Name="canvas" Width="1920" Height="1080">
<Image Name="img" Width="50" Height="50" />
</Canvas>
</Grid>
Your Event Handler:
void Reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
var reference = e.FrameReference.AcquireFrame();
using (var frame = reference.BodyFrameReference.AcquireFrame())
{
if (frame != null)
{
var body = frame.Bodies().Closest();
if (body != null)
{
JointType _start = JointType.SpineShoulder;
JointType _center = JointType.ShoulderRight;
JointType _end = JointType.ShoulderLeft;
double angle = body.Joints[_center].Angle(body.Joints[_start], body.Joints[_end]);
Point point = new Point();
ColorSpacePoint colorPoint = _sensor.CoordinateMapper.MapCameraPointToColorSpace(body.Joints[_center].Position);
point.X = float.IsInfinity(colorPoint.X) ? 0 : colorPoint.X;
point.Y = float.IsInfinity(colorPoint.Y) ? 0 : colorPoint.Y;
img.Source = new BitmapImage(new Uri("your-image-path", UriKind.RelativeOrAbsolute));
img.RenderTransformOrigin = new Point(0.5, 0.5);
img.RenderTransform = new RotateTransform(angle);
Canvas.SetLeft(img, point.X - img.Width / 2);
Canvas.SetTop(img, point.Y - img.Height / 2);
}
}
}
}
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I'm currently working on an exercise for my c# class. I am having some trouble with one particular part and would really appreciate some help.
I am working on an exercise in which we are given an incomplete project file.
The project has multiple classes, this class is for controlling squares that are placed on the board.
namespace HareAndTortoise {
public partial class SquareControl : PictureBox {
public const int SQUARE_SIZE = 100;
private Square square; // A reference to the corresponding square object
private BindingList<Player> players; // References the players in the overall game.
private bool[] containsPlayers = new bool[6];//HareAndTortoiseGame.MAX_PLAYERS];
public bool[] ContainsPlayers {
get {
return containsPlayers;
}
set {
containsPlayers = value;
}
}
// Font and brush for displaying text inside the square.
private Font textFont = new Font("Microsoft Sans Serif", 8);
private Brush textBrush = Brushes.White;
public SquareControl(Square square, BindingList<Player> players) {
this.square = square;
this.players = players;
// Set GUI properties of the whole square.
Size = new Size(SQUARE_SIZE, SQUARE_SIZE);
Margin = new Padding(0); // No spacing around the cell. (Default is 3 pixels.)
Dock = DockStyle.Fill;
BorderStyle = BorderStyle.FixedSingle;
BackColor = Color.CornflowerBlue;
SetImageWhenNeeded();
}
private void SetImageWhenNeeded()
{
if (square is Square.Win_Square)
{
LoadImageFromFile("Win.png");
textBrush = Brushes.Black;
}
else if (square is Square.Lose_Square)
{
LoadImageFromFile("Lose.png");
textBrush = Brushes.Red;
}
else if (square is Square.Chance_Square)
{
LoadImageFromFile("monster-green.png");
}
else if (square.Name == "Finish")
{
LoadImageFromFile("checkered-flag.png");
}
else
{
// No image needed.
}
}
private void LoadImageFromFile(string fileName) {
Image image = Image.FromFile(#"Images\" + fileName);
Image = image;
SizeMode = PictureBoxSizeMode.StretchImage; // Zoom is also ok.
}
protected override void OnPaint(PaintEventArgs e) {
// Due to a limitation in WinForms, don't use base.OnPaint(e) here.
if (Image != null)
e.Graphics.DrawImage(Image, e.ClipRectangle);
string name = square.Name;
// Create rectangle for drawing.
float textWidth = textFont.Size * name.Length;
float textHeight = textFont.Height;
float textX = e.ClipRectangle.Right - textWidth;
float textY = e.ClipRectangle.Bottom - textHeight;
RectangleF drawRect = new RectangleF(textX, textY, textWidth, textHeight);
// When debugging this method, show the drawing-rectangle on the screen.
//Pen blackPen = new Pen(Color.Black);
//e.Graphics.DrawRectangle(blackPen, textX, textY, textWidth, textHeight);
// Set format of string.
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Far; // Right-aligned.
// Draw string to screen.
e.Graphics.DrawString(name, textFont, textBrush, drawRect, drawFormat);
// Draw player tokens (when any players are on this square).
const int PLAYER_TOKENS_PER_ROW = 3;
const int PLAYER_TOKEN_SIZE = 30; // pixels.
const int PLAYER_TOKEN_SPACING = (SQUARE_SIZE - (PLAYER_TOKEN_SIZE * PLAYER_TOKENS_PER_ROW)) / (PLAYER_TOKENS_PER_ROW - 1);
for (int i = 0; i < containsPlayers.Length; i++) {
if (containsPlayers[i]) {
int xPosition = i % PLAYER_TOKENS_PER_ROW;
int yPosition = i / PLAYER_TOKENS_PER_ROW;
int xPixels = xPosition * (PLAYER_TOKEN_SIZE + PLAYER_TOKEN_SPACING);
int yPixels = yPosition * (PLAYER_TOKEN_SIZE + PLAYER_TOKEN_SPACING);
Brush playerTokenColour = players[i].PlayerTokenColour;
e.Graphics.FillEllipse(playerTokenColour, xPixels, yPixels, PLAYER_TOKEN_SIZE, PLAYER_TOKEN_SIZE);
}
}//endfor
}
}
}
The program trips up at:
else if (square.Name == "Finish")
{
LoadImageFromFile("checkered-flag.png");
}
I know it is because of square.name but from going through the code, I cant see why square.Name is not recognizable.
Square is passed from another class using this method
private void SetUpGuiGameBoard()
{
for (int i = 0; i <= 55; i++)
{
Square q = Board.GetGameBoardSquare(i);
SquareControl sq = new SquareControl(q, null);
int coloumn;
int row;
if (i == 0)
{
BackColor = Color.BurlyWood;
}
if (i == 55)
{
BackColor = Color.BurlyWood;
}
MapSquareNumToTablePanel(i, out coloumn, out row);
tableLayoutPanel1.Controls.Add(sq, coloumn, row);
}
and Squares are created in this class
private static Square[] gameBoard = new Square[56];
static public void SetUpBoard()
{
for (int i = 1; i == 55; i++)
{
gameBoard[i] = new Square("Ordinary Square", i);
}
gameBoard[0] = new Square("Start", 0);
gameBoard[4] = new Square.Lose_Square("Lose Square", 4);
gameBoard[5] = new Square.Chance_Square("Chance Square", 5);
gameBoard[9] = new Square.Win_Square("Win Square", 9);
gameBoard[11] = new Square.Chance_Square("Chance Square", 11);
gameBoard[14] = new Square.Lose_Square("Lose Square", 14);
gameBoard[17] = new Square.Chance_Square("Chance Square", 17);
gameBoard[19] = new Square.Win_Square("Win Square", 19);
gameBoard[24] = new Square.Lose_Square("Lose Square", 24);
gameBoard[29] = new Square.Win_Square("Win Square", 29);
gameBoard[34] = new Square.Lose_Square("Lose Square", 34);
gameBoard[35] = new Square.Chance_Square("Chance Square", 35);
gameBoard[39] = new Square.Win_Square("Win Square", 39);
gameBoard[44] = new Square.Lose_Square("Lose Square", 44);
gameBoard[47] = new Square.Chance_Square("Chance Square", 47);
gameBoard[49] = new Square.Win_Square("Win Square", 49);
gameBoard[53] = new Square.Chance_Square("Chance Square", 53);
gameBoard[55] = new Square("Finish", 56);
}
public static Square GetGameBoardSquare(int n)
{
return gameBoard[n];
}
public static Square StartSquare()
{
return gameBoard[0];
}
public static Square NextSquare(int n)
{
return gameBoard[(n+1)];
}
}
The answer already provided is the best way for prevention of any null reference exception. For more clarification I can suggest you to check the call stack at the point the debugger reaches the SquareControl constructor. At this point you should check why the Square object being passed in is a 'NULL'. That will lead you to the root cause of the problem. Hope this helps.
I'm using DrawingContext.DrawText and DrawingContext.PushTransfrom to create rotated text on Visual Layer in WPF but as you see in the image below, the rotated text is rather blurry in some areas of the image..
Is there any option I can use to improve this? The Arial font is used for the text.
public class BeamTextDrawing : FrameworkElement
{
private readonly VisualCollection _visuals;
public BeamTextDrawing(double scale)
{
if (scale <= 0)
{
scale = 1;
}
var typeface = Settings.BeamTextTypeface;
var cultureinfo = Settings.CultureInfo;
var flowdirection = Settings.FlowDirection;
var textsize = Settings.BeamTextSize / scale;
var beamtextcolor = Settings.InPlanBeamTextColor;
_visuals = new VisualCollection(this);
foreach (var beam in Building.BeamsInTheElevation)
{
var drawingVisual = new DrawingVisual();
using (var dc = drawingVisual.RenderOpen())
{
var text = Convert.ToString(beam.Section.Id);
//text = scale.ToString();
var ft = new FormattedText(text, cultureinfo, flowdirection,
typeface, textsize, beamtextcolor)
{
TextAlignment = TextAlignment.Center
};
var x1 = beam.ConnectivityLine.I.X;
var y1 = beam.ConnectivityLine.I.Y;
var x2 = beam.ConnectivityLine.J.X;
var y2 = beam.ConnectivityLine.J.Y;
var v1 = new Point(x2, y2) - new Point(x1, y1);
var v2 = new Vector(1, 0);
var hwidth = textsize;
var l = Geometrics.GetOffset(x1, y1, x2, y2, hwidth + 5/scale);
var angle = Vector.AngleBetween(v1, v2);
var x = 0.5 * (l.X1 + l.X2);
var y = 0.5 * (l.Y1 + l.Y2);
var r = new RotateTransform(angle, x, SelectableModel.FlipYAxis(y));
dc.PushTransform(r);
dc.DrawText(ft, SelectableModel.FlipYAxis(x, y));
}
_visuals.Add(drawingVisual);
}
}
protected override Visual GetVisualChild(int index)
{
return _visuals[index];
}
protected override int VisualChildrenCount
{
get
{
return _visuals.Count;
}
}
}
Update:
Here is the image after using this code:
TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);
I'm still getting blurry results. Look at the middle beam text at the lower part of the image.
Check out this article - Pixel Snapping in WPF Applications
Since WPF uses device independent pixels it can create irregular edge rendering due to anti-aliasing when it undergoes transformation. Pixel snapping is a means to suppress these visual artifacts by applying small offsets to the geometry of the visual to align the geometry to device pixels.
Setting the SnapsToDevicePixels Property to "True" for your UI elements should be able to fix your issue.
need a Method to return a variable Texturebrush. The method need to be overloaded with:
Size AreaSize,
int HorizontalSeperatorCount,
int VerticalSeperatorCount,
int SeperatorWidth,
Brush Seperatorbackground,
Brush Rectanglebackground,
The size of the rectangle will be calculated automatically
These example have following values
Size AreaSize = new Size(100, 100),
int HorizontalSeperatorCount = 1,
int VerticalSeperatorCount = 1,
int SeperatorWidth = 10,
Brush Seperatorbackground = Brushes.Grey,
Brush Rectanglebackground = Brushes.Red
The second example have a different VerticalSeperatorCount
Size AreaSize = new Size(100, 100),
int HorizontalSeperatorCount = 1,
int VerticalSeperatorCount = 3,
int SeperatorWidth = 10,
Brush Seperatorbackground = Brushes.Grey,
Brush Rectanglebackground = Brushes.Red
The signatur of the method
public static TextureBrush GetTextureBrush(Size areaSize, int horizontalSeperator, int verticalSeperatorCount, int seperatorWidth, Brush seperatorBackground, Brush rectangleBackground)
The Texturebrush will be used to fill a window
I'm not the best at the drawing stuff.
I would be very very happy about a solution.
public static TextureBrush GetSeperatorBrush(Size areaSize,
int horizontalSeperatorCount,
int verticalSeperatorCount,
int seperatorWidth,
Brush rectangleBackground)
{
var horizontalRectangleCount = horizontalSeperatorCount + 1.0f;
var verticalRectangleCount = verticalSeperatorCount + 1.0f;
var horizontalSeperatorBreadths = (horizontalSeperatorCount + 2.0f) * seperatorWidth;
var verticalSeperatorBreadths = (verticalSeperatorCount + 2.0f) * seperatorWidth;
var rectangleWidth = (areaSize.Width - verticalSeperatorBreadths) / verticalRectangleCount;
var rectangleHeight = (areaSize.Height - horizontalSeperatorBreadths) / horizontalRectangleCount;
var bitmap = new Bitmap((int)Math.Ceiling(rectangleWidth + seperatorWidth), (int)Math.Ceiling(rectangleHeight + seperatorWidth));
var graphics = Graphics.FromImage(bitmap);
var rectanglePoints = new[] { new PointF(seperatorWidth, seperatorWidth),
new PointF(seperatorWidth + rectangleWidth, seperatorWidth),
new PointF(seperatorWidth + rectangleWidth, seperatorWidth + rectangleHeight),
new PointF(seperatorWidth, seperatorWidth + rectangleHeight),
new PointF(seperatorWidth, seperatorWidth)};
graphics.FillPolygon(rectangleBackground, rectanglePoints);
graphics.Dispose();
var textureBrush = new TextureBrush(bitmap, System.Drawing.Drawing2D.WrapMode.Tile);
return textureBrush;
}
Make some trouble with to much seperators because of the rounding . It's ok for me.