Scaling part of a PlantUML diagram - plantuml

Scaling is controlled by the scale keyword. I'm curious if it's possible to scale part of a PlantUML diagram somehow or at the very least: scale the font size for part of a diagram.
It doesn't seem like scale may be scoped to part of a diagram (and I have not found any examples that suggest that is possible). Conceivably it might be used in the following manner, but this example scales the entire image:
#startuml
package "Some Group" {
scale 0.5
HTTP - [First Component]
[Another Component]
}
node "Other Groups" {
FTP - [Second Component]
[First Component] --> FTP
}
#enduml

I do not think currently there is a way to achieve it. From the source code of the PlantUML scale command:
protected CommandExecutionResult executeArg(AbstractPSystem diagram, LineLocation location, RegexResult arg) {
double scale = Double.parseDouble(arg.get("SCALE", 0));
if (scale == 0) {
return CommandExecutionResult.error("Scale cannot be zero");
}
if (arg.get("DIV", 0) != null) {
final double div = Double.parseDouble(arg.get("DIV", 0));
if (div == 0) {
return CommandExecutionResult.error("Scale cannot be zero");
}
scale /= div;
}
diagram.setScale(new ScaleSimple(scale));
return CommandExecutionResult.ok();
}
The location of scale is not used, and PlantUML just sets the scale of diagram by diagram.setScale(new ScaleSimple(scale)).
You can verify this kind of action by:
#startuml
package "Some Group" {
scale 0.5
HTTP - [First Component]
[Another Component]
}
node "Other Groups" {
scale 5
FTP - [Second Component]
[First Component] --> FTP
}
#enduml
The scale 0.5 is overridden by the scale 5 command.

Related

Exporting TeeChart as Image

We are using TeeChart for .net (Version 4.1.2013.7302) in our forms application.
One of the charts in our product has Y Axis scrolling enabled. This makes some portion of chart visible at a given instance. To see other part of chart, user needs to use the scrollbar. A separate scrollbar is used instead of axis scrollbar, as there will be a adjoining grid control and; both chart & grid are expected to be scrolled using common scrollbar. Following is the sample form image depicting the scenario:
We are using TeeChart's export functionality to export this chart as an image. But since chart has scrolling enabled (i.e. minimum of chart is not visible by default); TeeChart is exporting only visible portion of Chart, and not the entire chart. Following is the image of chart exported:
Please suggest if there exists any way to export the entire chart as an image, and not just the visible portion of it?
Thanks in Advance.
Here's a fuller version of Yeray's code that fills out the exported image to the size of the full, mainly non-visible chart:
private void button11_Click(object sender, EventArgs e)
{
//get zoomed axis min maxes
double xtmpMin = tChart1.Axes.Bottom.Minimum;
double xtmpMax = tChart1.Axes.Bottom.Maximum;
double ytmpMin = tChart1.Axes.Left.Minimum;
double ytmpMax = tChart1.Axes.Left.Maximum;
//how many pixels are plotted for the axes' ranges
int yPixelRange = tChart1.Axes.Left.CalcPosValue(tChart1.Axes.Left.Minimum)-tChart1.Axes.Left.CalcPosValue(tChart1.Axes.Left.Maximum);
int xPixelRange = tChart1.Axes.Bottom.CalcPosValue(tChart1.Axes.Bottom.Maximum) - tChart1.Axes.Bottom.CalcPosValue(tChart1.Axes.Bottom.Minimum);
//get the chart header/footer space to re-apply to chart
int yMargins = tChart1.Bounds.Height - yPixelRange;
int xMargins = tChart1.Bounds.Width - xPixelRange;
//how many pixels are we getting per axis scale
double pixelsPerYAxisInt = yPixelRange / (ytmpMax - ytmpMin);
double pixelsPerXAxisInt = xPixelRange / (xtmpMax - xtmpMin);
//what increment are we at. Note. To get this back we may need to mod font size, min separation
double yInc = tChart1.Axes.Left.CalcIncrement;
double xInc = tChart1.Axes.Bottom.CalcIncrement;
//now reset auto axes before plotting full chart. Could use other criteria here
tChart1.Axes.Left.Automatic = true;
tChart1.Axes.Bottom.Automatic = true;
//Repaint full Chart (necessary for positioning calcs)
tChart1.Draw();
//set increments on full scales (note Chart will try to set them,
//but if it can't you have the last word with label separation, font size, etc)
tChart1.Axes.Left.Increment = yInc;
tChart1.Axes.Bottom.Increment = xInc;
//dimension chart for export
double fullYRange = tChart1.Axes.Left.Maximum - tChart1.Axes.Left.Minimum;
double fullXRange = tChart1.Axes.Bottom.Maximum - tChart1.Axes.Bottom.Minimum;
int fullYSize = (int)((pixelsPerYAxisInt * fullYRange) + yMargins);
int fullXSize = (int)((pixelsPerXAxisInt * fullXRange) + xMargins);
//setup and export image
tChart1.Export.Image.PNG.Width = fullXSize;
tChart1.Export.Image.PNG.Height = fullYSize;
tChart1.Export.Image.PNG.Save(#"c:\mypath\chart.png");
//reset screen chart to where it was
tChart1.Axes.Bottom.SetMinMax(xtmpMin, xtmpMax);
tChart1.Axes.Left.SetMinMax(ytmpMin, ytmpMax);
}
There are many ways to optimise that code, Axis does have an iRange that I haven't tried, and some of the steps can be brought together but I hope they are clear and useful and give you something of what you're looking for.
You can manually adjust your axis scale, export your chart and then restore your axis. Ie (if 0 - 4.25 in the bottom axis is the "entire chart"):
double tmpMin = tChart1.Axes.Bottom.Minimum;
double tmpMax = tChart1.Axes.Bottom.Maximum;
tChart1.Axes.Bottom.SetMinMax(0, 4.25);
tChart1.Export.Image.JPEG.Save(myFileName);
tChart1.Axes.Bottom.SetMinMax(tmpMin, tmpMax);

xy plot of renderer label points(data points) are overlapping with line chart of TimeSeriesChart of jfreechart

I am using Jfreechart API 1.0.8 to generate the TimeSeriesChart(line chart).
when I am generating the chart, i am facing the problem of overlapping.
Here I am trying to display the rendered points(graph rendered points), by using XYLineAndShapeRenderer with StandardXYItemLabelGenerator.
When the points are displayed, the data-point is overlapping with the generated line chart (graph).
I am taking X-Axis as time, and y-Axis as revenue of organization, and i am using line chart here.
I'm displaying the points as discussed below.
By using "renderer.setBasePositiveItemLabelPosition" method, globally i am setting the position of graph points(data points) inside the xyplot rendered chart while considering the rendered "ItemLabelAnchor".
I am sending my sample code here:
chart = ChartFactory.createTimeSeriesChart("", "", "", newxyseries, false, true, false);
renderer = new XYLineAndShapeRenderer();
renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator("{2}", monthDate, formatSymbol));
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.TOP_RIGHT));
chart.getXYPlot().setRenderer(renderer);
But when I am generating the graph chart using Ms-office of Excel tools, there is no problem of overlapping of labels, the points are displayed in an effective manner without any overlapping.
JFreeChart doesn't do any overlap detection for item labels. It would be a great feature to have, but nobody has written the code to do it.
you have to refrain the labels to get overlap by define your own algo or logic because as the graph keep growing sooner the labels will start to get overlap.
the key is to make some or alternative labels transparent so that no overlapping occur
For example in the following P.O.C, only 35 labels will be drawn but the tick of graphs will remai same just the labels will reduce
CategoryAxis domainAxis = plot.getDomainAxis();
CategoryLabelPositions pos = domainAxis.getCategoryLabelPositions();
double size = plot.getCategories().size();
double occurence = Math.ceil(plot.getCategories().size() / 20);
double interval = Math.ceil(plot.getCategories().size() / 20);
for (int i = 1; i <= plot.getCategories().size(); i++) {
if (plot.getCategories().size() > 35) {
if(plot.getCategories().size()>35 && plot.getCategories().size()<250) {
if(i%8==0){
String cat_Name = (String) plot.getCategories().get(i-1);
} else{
String cat_Names = (String) plot.getCategories().get(i-1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));
}
}else if(plot.getCategories().size()>250){
[![enter image description here][1]][1]
if (i == occurence) {
String cat_Name = (String) plot.getCategories().get(i - 1);
if (occurence + interval >= size) {
// occurence=size;
} else {
occurence = occurence + interval;
}
} else {
String cat_Names = (String) plot.getCategories().get(i - 1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0, 0, 0, 0));
}
}
}
Below line will make label transparent
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));

Scale components in FlowLayout as big as possible

How can I scale PictureBox components to best fit the given space on the screen while keeping their aspect ratio (interdependent of the actual image or its SizeMode) ?
I tested setting the Dock of the FlowLayout and the PictureBox to Fill. I also tested using a Panel as a wrapper and tested different settings for AutoSize and AutoSizeMode.
To give more information about the background: I want to dynamically add and remove images in the viewport of the application, so a TableLayout is in the first step sort of to static. I have to admit I'm was also thinking of calculating the size an position manually - or to dynamically adapt the row and column count of the TableLayout - but it seems to me prone to errors. I thought having a FlowLayout and automatically sized components should be the correct way - but it seems not to work that way. (To speak as web developer, I simply want to "float the images left", "with width and height set to 'auto'" and no scrolling.)
The images should visualize this a bit: the first figure should point out the layout, if there is only one PictureBox - it takes the whole space (or as big as possible with the given aspect ratio). The second shows how I would like the Layout to be, if there are two (three or four) images. The third figure is showing basically a resized window with three (to six) images.
Is there some point I'm missing?
This code snippet do this:
It arranges the visible controls inside a container in respect of the aspect ratio (see R variable in the code), and uses the container margin values to get horizontal and vertical gaps between items. The padding of the container is also handled.
public static void Arrange(Control container)
{
var H = container.DisplayRectangle.Height;
var W = container.DisplayRectangle.Width;
var N = container.Controls.OfType<Control>().Count(c => c.Visible);
var R = 4 / 3d; // item aspect ratio
var margin = container.Margin;
var padding = container.Padding;
var horizontalGap = margin.Left + margin.Right;
var verticalGap = margin.Top + margin.Bottom;
if (N == 0)
return;
var bestSizedItem = (
// Try n rows
Enumerable.Range(1, N).Select(testRowCount =>
{
var testItemHeight = (H - verticalGap * (testRowCount - 1)) / testRowCount;
return new
{
testColCount = (int)Math.Ceiling((double)N / testRowCount),
testRowCount = testRowCount,
testItemHeight = (int)testItemHeight,
testItemWidth = (int)(testItemHeight * R)
};
})
// Try n columns
.Concat(
Enumerable.Range(1, N).Select(testColCount =>
{
var testItemWidth = (W - horizontalGap * (testColCount - 1)) / testColCount;
return new
{
testColCount = testColCount,
testRowCount = (int)Math.Ceiling((double)N / testColCount),
testItemHeight = (int)(testItemWidth / R),
testItemWidth = (int)testItemWidth
};
})))
// Remove when it's too big
.Where(item => item.testItemWidth * item.testColCount + horizontalGap * (item.testColCount - 1) <= W &&
item.testItemHeight * item.testRowCount + verticalGap * (item.testRowCount - 1) <= H)
// Get the biggest area
.OrderBy(item => item.testItemHeight * item.testItemWidth)
.LastOrDefault();
Debug.Assert(bestSizedItem != null);
if (bestSizedItem == null)
return;
int x = container.DisplayRectangle.X;
int y = container.DisplayRectangle.Y;
foreach (var control in container.Controls.OfType<Control>().Where(c => c.Visible))
{
control.SetBounds(x, y,
bestSizedItem.testItemWidth,
bestSizedItem.testItemHeight);
x += bestSizedItem.testItemWidth + horizontalGap;
if (x + bestSizedItem.testItemWidth - horizontalGap > W)
{
x = container.DisplayRectangle.X;
y += bestSizedItem.testItemHeight + verticalGap;
}
}
}
I put this snippet on Gist so you can contribute if you wish.

WPF FlowDocument: force calculation of height etc. "off screen"

My target: a DocumentPaginator which takes a FlowDocument with a table, which splits the table to fit the pagesize and repeat the header/footer (special tagged TableRowGroups) on every page.
For splitting the table I have to know the heights of its rows.
While building the FlowDocument-table by code, the height/width of the TableRows are 0 (of course). If I assign this document to a FlowDocumentScrollViewer (PageSize is set), the heights etc. are calculated. Is this possible without using an UI-bound object? Instantiating a FlowDocumentScrollViewer which is not bound to a window doesn't force the pagination/calculation of the heights.
This is how I determine the height of a TableRow (which works perfectly for documents shown by a FlowDocumentScrollViewer):
FlowDocument doc = BuildNewDocument();
// what is the scrollviewer doing with the FlowDocument?
FlowDocumentScrollViewer dv = new FlowDocumentScrollViewer();
dv.Document = doc;
dv.Arrange(new Rect(0, 0, 0, 0));
TableRowGroup dataRows = null;
foreach (Block b in doc.Blocks)
{
if (b is Table)
{
Table t = b as Table;
foreach (TableRowGroup g in t.RowGroups)
{
if ((g.Tag is String) && ((String)g.Tag == "dataRows"))
{
dataRows = g;
break;
}
}
}
if (dataRows != null)
break;
}
if (dataRows != null)
{
foreach (TableRow r in dataRows.Rows)
{
double maxCellHeight = 0.0;
foreach (TableCell c in r.Cells)
{
Rect start = c.ElementStart.GetCharacterRect(LogicalDirection.Forward);
Rect end = c.ElementEnd.GetNextInsertionPosition(LogicalDirection.Backward).GetCharacterRect(LogicalDirection.Forward);
double cellHeight = end.Bottom - start.Top;
if (cellHeight > maxCellHeight)
maxCellHeight = cellHeight;
}
System.Diagnostics.Trace.WriteLine("row " + dataRows.Rows.IndexOf(r) + " = " + maxCellHeight);
}
}
Edit:
I added the FlowDocumentScrollViewer to my example. The call of "Arrange" forces the FlowDocument to calculate its heights etc. I would like to know, what the FlowDocumentScrollViewer is doing with the FlowDocument, so I can do it without the UIElement. Is it possible?
My guess would be no, you can't do it without a UIElement.
FlowDocument, by itself, doesn't actually render anything. Looking at the type in relector it looks like it is just a data type. Its about like having a string and wanting to know its size when rendered ... can't really do it without doing some kind of measure pass.
I don't know for sure, but you might get better performance in the arrange pass by passing in Double.PositiveInfinity for the size rather than 0. At least then it won't have to worry about measuring 'n' line breaks.

Piemenu for WPF

Are there any working piemenu controls for WPF?
I've found this in my favorite , you can take a look at :
This
have a nice day.
This question is probably long dead, but just a note that the control Thomas M posted, while awesome, has a major issue: You need to mouse over and click on the actual item instead of the pie slice. This means that the pie slices are not completely adjacent and IMO defeats a lot of the clickability (Frits's law) advantages of the control. So while it looks like a pie menu, it really just positions everything radially.
I ended up doing this:
private static Path makeDeliciousKeyLimePieSlice(double innerRadius, double outerRadius,
double startAngle, double endAngle, Vector ofs)
{
Point p1 = new Point(Math.Cos(endAngle) * innerRadius, Math.Sin(endAngle) * innerRadius) + ofs;
Point p2 = new Point(Math.Cos(startAngle) * innerRadius, Math.Sin(startAngle) * innerRadius) + ofs;
Point p3 = new Point(Math.Cos(startAngle) * outerRadius, Math.Sin(startAngle) * outerRadius) + ofs;
Point p4 = new Point(Math.Cos(endAngle) * outerRadius, Math.Sin(endAngle) * outerRadius) + ofs;
PathFigure fig = new PathFigure(p1, new PathSegment[] {
new ArcSegment(p2, new Size(innerRadius, innerRadius), endAngle - startAngle, false, SweepDirection.Counterclockwise, true),
new LineSegment(p3, true),
new ArcSegment(p4, new Size(outerRadius, outerRadius), startAngle - endAngle, false, SweepDirection.Clockwise, true),
}, true).GetAsFrozen();
return new Path { Data = new PathGeometry(new[] { fig }).GetAsFrozen() };
}
This will create a "slice" of the pie. You can style this how you want if you want a true pie menu. Another option is to make it transparent (set the fill to Brushes.Transparent; it must have a fill to be hit-test visible), which looks good for radial context menus. Here's my WIP after about half an hour's work (I know the spacing sucks):
alt text http://public.blu.livefilestore.com/y1pdW5ibqWquKGosMSch9C5KmOTKkiZ35mAI7iFKKUKf3cm7TGSquXhO8hkkL9Ln6Z3tKn74u67C27Qb_AIWQxzhg/radial.png?psid=1
EDIT: ah; the cursor doesn't appear in the shot -- basically, if you use the path overlay, you can have the mouse outside the actual control but still have it highlighted.
This control needs a bit of work still but it's a great starting point and supports multiple levels of items. (ie: a hierarchy) Check it out here

Resources