I have a list of objects which form a mathematical set. So I want to display them in an ellipse like this:
If I then add a new object, the ellipse should grow and the 6th element should be placed inside the ellipse.
I thought I could solve this problem with an iteration. At first place object1 and the Name of set and draw the ellipse. Secondly add object2 and let the ellipse grow, and so on.
But I already struggled to find a proper solution for the arrangement of the first step, not to mention the growth of the ellipse in the second step. My problem with the first step is, that the ellipse should be smaller when Name of set and object1 have a small string representation. And the ellipse should be bigger, if Name of set and object1 have a longer string representation.
Or did I get somewhere on the wrong track? Maybe there is already a (framework?) solution for this problem, which I overlook.
Create an integer property called MyCount.
Create integer constants OriginalWidth and OriginalHeight.
Create integer variables ScaledWidth and ScaledHeight.
When your set is created add mySet.Name.Count() to MyCount.
When you add an object, add myObject.SomeProperty.ToString().Count() to MyCount.
When you remove an object, subtract myObject.SomeProperty.ToString().Count() from MyCount.
When MyCount is changed, recalculate ScaledWidth and ScaledHeight - something like ScaledWidth = OriginalWidth * (MyCount * [insert number here])
In order to keep your ellipse in the correct perspective your Ellipse's Height and Width will be based on constant values multiplied by a scaling factor, which is MyCount. You need 4 integer values: OriginalWidth, OriginalHeight, ScaledWidth and ScaledHeight. OriginalWidth and OriginalHeight are integer constants defining the minimum width and height of your Ellipse. ScaledWidth and ScaledHeight are integer variables which your Ellipse's Height and Width properties are bound to.
Every time a new object is added or removed to/from your set the number of characters in its SomeProperty is added to MyCount. Every time MyCount changes it triggers the recalculation of ScaledWidth and ScaledHeight. Binding your Ellipse's Width and Height properties to ScaledWidth and ScaledHeight, and implementing INotifyPropertyChanged on these properties, should scale your Ellipse accordingly.
UPDATE
The algorithm posted here: http://programming.nullanswer.com/forum/11925 would appear to address placing your child elements within your parent ellipse. I've substituted ellipse for square/circle where appropriate:
Pick a random point inside the [parent ellipse] & instance a [very low Width/Height ellipse] at that point.
Grow the radius of that [ellipse] till it satisfies these criteria:
does not overlap any of the other chosen [ellipses].
does not extend outside the [parent ellipse].
Choose the [ellipse] if it satisfies these criteria.
Related
I am currently working on a module to create charts to display data.
I use System.Windows.Forms.DataVisualization.Charting.Chart.
I have two striplines showing the the average result we got and another one showing what we want.
So far I was really happy with what I had but I want to add explicit arrow to point these lines. And I can't figure out how to do it.
I saw that Line Annotation might be of help but I couldn't find a way to do what I wanted.
Here is an example of what I would like to do :
You have a choice of using
Annotations or
GDI+ drawing
In both cases the challenge is to get the positions right.
The more natural way to go is using Annotations, so let's look at this first:
There are various types but different capabilities; text can be displayed by RectangleAnnotation or a TextAnnotation. Lines and arrowheads can only be displayed by LineAnnotations. So we need a pair of Line- plus TextAnnotation for each of your two lines.
Like many other chart elements annotations are positioned in percentages of their respective containers; this makes things rather tricky at times.
To place a line annotation all to the right of the chart you could set its X property to 100; to let it go to the left you set the width to a negative number. The problems are starting after that..
To find out where the right edge of the ChartArea is you need to code the Pre- or PostPaint event and use the ToRectangleF method.
To find out the y-value you will want to calculate it from a data value; for this you can use the AxisY.ValueToPixelPosition method, which converts to pixels, from which you can calculate the percantage using the chart's ClientArea along with the ChartArea percentage size.
Complicated? Yup. Annotations get a lot simpler to use if you can anchor them to a certain DataPoint; but yours are outside the ChartArea..
Here is a function that should help when doing the calculations:
double PercentFromValue(Chart chart, ChartArea ca, double value)
{
Axis ay = ca.AxisY;
RectangleF car = ca.Position.ToRectangleF();
double py = ay.ValueToPixelPosition(value);
int caHeight = (int)(chart.ClientRectangle.Height * car.Height / 100f);
return 100d * py / caHeight;
}
Note that it will only work reliably when called from of of the Pre/PostPaint events..
So this is an example of a PrePaint event that positions a LineAnnotation lAnn:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
Rectangle cr = chart1.ClientRectangle;
ChartArea ca = chart1.ChartAreas[0];
RectangleF car = ca.Position.ToRectangleF();
lAnn.Width = car.Width - lAnn.X;
lAnn.Y = PercentFromValue(chart1, ca, someDataValue);
}
When you insert a valid DataPoint YValue the starting y-position will be set. You can play with it until you find a nice combination of setting the four position properties..
When creating and adding the four(!) annotations you may want to keep class level references, so you won't have to refer to them from the Annotations collection..
For the LineAnnotation you will want to set the linewidth, color and the capstyle, either using the EndCap or the StartCap:
lAnn.EndCap = LineAnchorCapStyle.Arrow;
GDI+ drawing is more straight-forward, provided you know where you want to draw the lines and the text.
It is also done in the PrePaint event, again using the ValueToPixelPosition to find the pixelposition of the two data lines.. Other than that is all the usual stuff with Graphics.DrawLine, a Pen with and Start- or EndCap and Graphics.DrawString or maybe TextRenderer.DrawText..
I'm frankly not sure which way I would choose..
create triangle image and set the marker image as:
Chart1.Series.Points.AddXY(0, 10);
Chart1.Series.Points.AddXY(20,10);
Chart1.Series[0].Points[1].MarkerImage = "TriangleImage.bmp";
Chart1.Series[0].Points[1].MarkerImageTransparentColor = Color.White;
or
Chart1.Series[0].Points[1].MarkerStyle = MarkerStyle.Triangle;
Hi Mathmeticians out there.
I am a little stumped and I was wondering if there was any sort of algorithm that could help me.
First the conceptual problem, Lets say I have a bunch of boxes that lie along an X axis. I want to be able to choose an arbitrary point A on the axis and have everything on the left scaled to 95% of its original width and position and to compensate, everything on the right will have to be scaled to 105%. The width of the resulting boxes is easy to calculate since it is the original width times the scale. The problem I am having is how to calculate the gap which has now been created at point A so that I can shift the second part left to close that gap.
Furthermore, I would like to not only select a point A, but also a B and C, etc.. as well and be able to close their gaps likewise.
--The real reason I am asking--
Now for the actual problem (in case anyone else out there has gone through this.) I have a control in a C# Winforms app that was made by some programmer before I got here. The control can contain any number of child controls that each have their own relative coordinates as a percentage of the Width or Height (i.e. A control with a relative X coordinate of 0.5 will be placed halfway across the parent container.
We desperately need to support multiple monitors and the problem that I am having is that if you dock a control or toolbar next to our proprietary control then the ClientRectangle is smaller so it shifts around the child borders like so
My boss doesn't like that the lines shift over monitor boundaries and wants me to only mess with the lines on the same monitor where the window was docked. I have been able to get 90% of the way using the concept above, but I can't seem to get the re-spacing calculation right.
Here is a Mathematical model of what I think for calculating the gap.
Let's say that you have a starting point A, and lets define it as xA.
Now, let's define the boxes
//Box{x0,x1}
Boxes = {[B1]{0,100},[B2]{100,200},[B3]{200,400},[B4]{400,450},[B5]{450,700}}
Now we have 5 boxes on the X axis.
Let's define;
A = xA = 370;
TotalLength = 700;
If you divide 700 by 2, that makes 350 which makes the mid point, and 370 is bigger than the mid point value. So that is being said, in this case you would need to shift the elements on the left to right. The calculation of the gap is as the following;
IF(Midpoint < A)
Gap = ((A- Midpoint) * 100 ) / TotalLength //This is the gap in percent
ELSE
Gap = ((Midpoint - A) * 100) / TotalLength
This way, you can find the gap. The Axis you need to shift towards will need to be decided based on the point you are selecting, if the selected point less than the Mid point then shift to right, if higher shift to left (to the positive axis route).
I hope this helps.
Thank you for your help Surgeon. Unfortunately I wasn't able to find a solution using your method. On the bright side, I was able to find a solution. The trick was to treat the X coordinate as the width between the Client Rectangle's left edge and the X coordinate's position and calculate it similarly to the child width.
For more specifics, here's the algorithm I have come up with for dealing with the docking problem:
var clientOriginalWidth = what the width of the Client rectangle would be without docks
var clientCompressedWidth = the width of the client now
//Calculate the Compression Ratio for each screen as follows
foreach(var screen in Screens){
var widthOfClientRectOnScreenNow = how much of the client rectangle is on this screen
var widthOfClientRectOnScreenWithoutDocks = how much of the client rectangle was on the screen before the docks were there
var compressionRatio = (widthOfClientRectOnScreenNow / clientCompressedWidth) /
(widthOfClientRectOnScreenWithoutDocks / clientOriginalWidth);
//Assuming control.xScale and control.widthScale are initially 0
foreach(var control in ParentControl){
var controlBounds = where the control was when the client was full width
if(controlBounds.X > screen.right){
var percentOfXPositionOnScreen = screen.right - screen.left / control.x;
controlBounds.xScale += percentOfPositionOnScreen * compressionRatio;
}
else if(control.X > screen.left){
var percentOfXPositionOnScreen = control.x - screen.left / control.x;
controlBounds.xScale += percentOfPositionOnScreen * compressionRatio;
}
if(screen.intersects(controlBounds){
var percentControlIsOnScreen = what percent of the control's width was on this screen
control.widthScale += percentControlIsOnScreen * compressionRatio;
}
}
}
The position is then found by multiplying the original X coordinate by the scale (same for the width). Once the docks are removed, recalculate the scale. When all docks are removed, the scale should be 1.
I have left out some specifics to make it more of a generalized algorithm, but one should be able to work through this on their own system.
I am trying to get the distance in pixel between the top of the control and the middle of my vertical scrollbar (not all the scrollviewer, only the bar you can drag to scroll the control).
I don't understand which property i should use.
This is the code i wrote:
double barHeight = /*to do*/;
double barUpperEdge = scrollViewer.VerticalOffset;
double distance = barUpperEdge + (barHeight/2);
Another question: which is the mesurament unit of scrollViewer.VerticalOffset? If it isn't in pixel what cast should i do?
You can calculate that value from the IScrollInfo Interface. The ScrollViewer Class implements this interface and exposes the relevant properties that you need to use. As far as I can remember, you need to utilise three properties:
ExtentHeight - Gets a value that contains the vertical size of the extent.
ViewportHeight - Gets a value that contains the vertical size of the content's viewport.
VerticalOffset - Gets a value that contains the vertical offset of the scrolled content.
To explain a little further, the viewport relates to the visible area of the ScrollViewer and the Extent represents the total size of the scrollable area. The VerticalOffset describes the amount that the scrollable content has been scrolled. Using these three values, you should be able to calculate your required values that relate to the ScrollBar. Try something like this:
double barHeight = ViewportHeight * scrollviewer.ActualHeight / ExtentHeight;
UPDATE >>>
Please note that it is generally bad practice to use constant values in your calculations. Microsoft have exposed many common properties for this very reason. In your case, I believe that you can make use of the SystemParameters.VerticalScrollBarButtonHeight property, although you may need to add something to accommodate Padding and/or Margin values:
double barHeight = ViewportHeight * (scrollviewer.ActualHeight - 2 *
SystemParameters.VerticalScrollBarButtonHeight) / ExtentHeight;
You know... I've just thought of something... you may even be able to get your required thumb Height from these SystemParameters... you could try the SystemParameters.VerticalScrollBarThumbHeight Property, although I don't think that it will work with custom ScrollBars.
I load pdfdoc, by:
PdfReader pdfReader = new PdfReader(byteArray);
LocationTextExtractionStrategyEx st3 = new LocationTextExtractionStrategyEx();
PdfTextExtractor.GetTextFromPage(pdfReader, 1, st3);
Now I can get list of page elements from st3.TextLocationInfo. Every element has property TopLeft and BottomRight, they are Vector.
How can I get element position if I don't know max value of scale. I know that vector start on left bottom page corner but I don't know where is end because I don't know page size in the same scale like vector.
I can run
var pageSize = pdfReader.GetPageSize(1)
But values from vectors are bigger than pageSize Width and Height
On the other hand, can I load every char position on page?
LocationTextExtractionStrategyEx is not part of iTextSharp. I assume, therefore, you talk about the class provided in this answer. That class does nothing fancy with the positions. Thus, to respond to your issue:
I know that vector start on left bottom page corner but I don't know where is end because I don't know page size in the same scale like vector.
I can run
var pageSize = pdfReader.GetPageSize(1)
But values from vectors are bigger than pageSize Width and Height
First of all: the coordinates you get from LocationTextExtractionStrategyEx.TextLocationInfo indeed are to be interpreted in the context of pdfReader.GetPageSize.
There are two major causes why the vector values can be beyond Width and Height of the latter:
The rectangle returned by pdfReader.GetPageSize does not need to be based in (0,0). It could e.g. have x coordinates in 301..400 and y coordinates in 501..600. In that case height and width would both be 100 but all coordinates of points in that rectangle would be higher.
Thus, do not look at Width and Height but instead at Left, Bottom, Right, and Top.
Text may actually be outside the visible page and, therefore, have coordinates outside of pdfReader.GetPageSize.
For a final verdict please supply the PDF in question.
I read page size by
var pageSize = pdfReader.GetPageSize(1)
next I created
TextInfoLocation textLocation = new TextInfoLocation(textLine.TopLeft, textLine.BottomRight, this.PdfFilePageSize);
Properties .TopLeft and .BottomRight are vectors.
textLine is LocationTextExtractionStrategyEx.TextInfo object read from pdfReader by strategy.
Now text position in pixels form vectores I can get from:
double leftMargin = textLocation.LeftMargin;
I have an application where I need to dynamically build the content to a Canvas. Everything works just fine, but I am a little unsure of how I can set the y coordinates for the labels in the safest way. For example, I need to add three labels that are essentially lines of text. In Java Swing or C# GDI I would just query the the font for the line height and add that value to the y coordinate of the drawText command.
This is my code.
double y = 0.0;
_line1.Content = "Line1";
_line1.SetValue(Canvas.TopProperty, y);
_line1.SetValue(Canvas.LeftProperty, 0.0);
CanvasChart.Children.Add(_line1);
double textHeight = _line1.Height;
y += textHeight;
_line2.Content = "Line2";
_line2.SetValue(Canvas.TopProperty, 0.0);
_line2.SetValue(Canvas.LeftProperty, y);
CanvasChart.Children.Add(_line2);
This does not work because _line1.Height does not seem to be set to anything useful at this point. I suppose it has not rendered yet. The above code is in the loaded event for the window. ActualHeight does not help either.
Most code that I've seen seems to just set them to a hard coded value. That I suppose looks right on the developer's display, and you just hope looks good at other resolutions/DPI. In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
You must call the Measure method, specifying an infinite available size. This will update the DesiredSize of the control:
_line1.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double textHeight = _line1.DesiredSize.Height;
Another easy way to achieve the desired effect is to put the labels in a StackPanel.
In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
This is possible in WPF as well. The GlyphTypeface class provides the AdvanceWidths and AdvanceHeights properties for each character in a typeface. By using CharacterToGlyphMap, you can map a character to an index within the AdvanceHeights, and use that to determine the actual height of any character.
For a detailed example, see GlyphRun and So Forth.