|
|
777717 |
/*
|
|
|
777717 |
......... 2015 Ivan Mahonin
|
|
|
777717 |
|
|
|
777717 |
This program is free software: you can redistribute it and/or modify
|
|
|
777717 |
it under the terms of the GNU General Public License as published by
|
|
|
777717 |
the Free Software Foundation, either version 3 of the License, or
|
|
|
777717 |
(at your option) any later version.
|
|
|
777717 |
|
|
|
777717 |
This program is distributed in the hope that it will be useful,
|
|
|
777717 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
777717 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
777717 |
GNU General Public License for more details.
|
|
|
777717 |
|
|
|
777717 |
You should have received a copy of the GNU General Public License
|
|
|
777717 |
along with this program. If not, see <http: licenses="" www.gnu.org="">.</http:>
|
|
|
777717 |
*/
|
|
|
777717 |
|
|
|
777717 |
using System;
|
|
|
8cb222 |
using System.Collections.Generic;
|
|
|
8cb222 |
using System.Data.Linq;
|
|
|
8cb222 |
using System.Drawing;
|
|
|
8cb222 |
using System.IO;
|
|
|
8cb222 |
using System.Globalization;
|
|
|
8cb222 |
|
|
|
8cb222 |
namespace Diagram {
|
|
|
8cb222 |
|
|
|
8cb222 |
public class ActiveBlock {
|
|
|
8cb222 |
public Block block;
|
|
|
8cb222 |
public readonly List<activelink> links = new List<activelink>();</activelink></activelink>
|
|
|
8cb222 |
public PointF position;
|
|
|
8cb222 |
|
|
|
8cb222 |
public Font captionFont;
|
|
|
8cb222 |
public Font textFont;
|
|
|
8cb222 |
public float margin;
|
|
|
8cb222 |
public float padding;
|
|
|
8cb222 |
public Pen pen;
|
|
|
8cb222 |
public Brush brush;
|
|
|
8cb222 |
|
|
|
8cb222 |
public string caption;
|
|
|
8cb222 |
public string text;
|
|
|
8cb222 |
public SizeF captionSize;
|
|
|
8cb222 |
public SizeF textSize;
|
|
|
8cb222 |
public float distance;
|
|
|
8cb222 |
|
|
|
8cb222 |
public SizeF size;
|
|
|
8cb222 |
public SizeF clientSize;
|
|
|
8cb222 |
|
|
|
8cb222 |
public PointF getCenter() {
|
|
|
8cb222 |
return new PointF(
|
|
|
8cb222 |
position.X + 0.5f*size.Width,
|
|
|
8cb222 |
position.Y + 0.5f*size.Height );
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void measure() {
|
|
|
8cb222 |
float width = captionFont.GetHeight() * 15f;
|
|
|
8cb222 |
caption = TextUtils.wrap(block.caption, width, captionFont);
|
|
|
8cb222 |
captionSize = TextUtils.measure(caption, captionFont);
|
|
|
8cb222 |
text = TextUtils.wrap(block.text, width, textFont);
|
|
|
8cb222 |
textSize = TextUtils.measure(text, textFont);
|
|
|
8cb222 |
if (caption != "" && text != "")
|
|
|
8cb222 |
distance = 0.3f * Math.Max(captionFont.GetHeight(), textFont.GetHeight());
|
|
|
8cb222 |
clientSize = new SizeF(
|
|
|
8cb222 |
Math.Max(captionSize.Width, textSize.Width),
|
|
|
8cb222 |
captionSize.Height + distance + textSize.Height );
|
|
|
8cb222 |
size = new SizeF(clientSize.Width + 2f*padding, clientSize.Height + 2f*padding);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void draw(Graphics g) {
|
|
|
8cb222 |
Shapes.drawRoundRect(g, new RectangleF(position, size), 2f*padding, pen, brush);
|
|
|
8cb222 |
|
|
|
8cb222 |
g.DrawString(
|
|
|
8cb222 |
caption,
|
|
|
8cb222 |
captionFont,
|
|
|
8cb222 |
Brushes.Black,
|
|
|
8cb222 |
new RectangleF(
|
|
|
8cb222 |
position.X + 0.5f*(size.Width - captionSize.Width),
|
|
|
8cb222 |
position.Y + padding,
|
|
|
8cb222 |
captionSize.Width,
|
|
|
8cb222 |
captionSize.Height ));
|
|
|
8cb222 |
|
|
|
8cb222 |
g.DrawString(
|
|
|
8cb222 |
text,
|
|
|
8cb222 |
textFont,
|
|
|
8cb222 |
Brushes.Black,
|
|
|
8cb222 |
new RectangleF(
|
|
|
8cb222 |
position.X + padding,
|
|
|
8cb222 |
position.Y + padding + captionSize.Height + distance,
|
|
|
8cb222 |
clientSize.Width,
|
|
|
8cb222 |
clientSize.Height - captionSize.Height - distance ));
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
class LinkDesc {
|
|
|
8cb222 |
public ActiveLink link;
|
|
|
8cb222 |
public PointF src;
|
|
|
8cb222 |
public PointF dst;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
class SideDesc {
|
|
|
8cb222 |
public PointF a;
|
|
|
8cb222 |
public PointF b;
|
|
|
8cb222 |
public PointF normal;
|
|
|
8cb222 |
public readonly List<linkdesc> links = new List<linkdesc>();</linkdesc></linkdesc>
|
|
|
8cb222 |
|
|
|
8cb222 |
public SideDesc(PointF a, PointF b) {
|
|
|
8cb222 |
this.a = a;
|
|
|
8cb222 |
this.b = b;
|
|
|
8cb222 |
float l = Geometry.lineLength(a, b);
|
|
|
8cb222 |
normal = new PointF((b.Y - a.Y)/l, (a.X - b.X)/l);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void placeLinks() {
|
|
|
8cb222 |
PointF lt = position;
|
|
|
8cb222 |
PointF rb = new PointF(lt.X + size.Width, lt.Y + size.Height);
|
|
|
8cb222 |
PointF lb = new PointF(lt.X, rb.Y);
|
|
|
8cb222 |
PointF rt = new PointF(rb.X, lt.Y);
|
|
|
8cb222 |
PointF center = getCenter();
|
|
|
8cb222 |
|
|
|
8cb222 |
List<sidedesc> sides = new List<sidedesc>() {</sidedesc></sidedesc>
|
|
|
8cb222 |
new SideDesc(lt, rt),
|
|
|
8cb222 |
new SideDesc(rt, rb),
|
|
|
8cb222 |
new SideDesc(rb, lb),
|
|
|
8cb222 |
new SideDesc(lb, lt) };
|
|
|
8cb222 |
|
|
|
8cb222 |
foreach(ActiveLink link in links) {
|
|
|
8cb222 |
link.visible = false;
|
|
|
8cb222 |
LinkDesc linkDesc = new LinkDesc();
|
|
|
8cb222 |
linkDesc.link = link;
|
|
|
8cb222 |
if (link.src == this) {
|
|
|
8cb222 |
linkDesc.src = center;
|
|
|
8cb222 |
linkDesc.dst = link.dst.getCenter();
|
|
|
8cb222 |
} else {
|
|
|
8cb222 |
linkDesc.src = link.src.getCenter();
|
|
|
8cb222 |
linkDesc.dst = center;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
foreach(SideDesc side in sides) {
|
|
|
8cb222 |
PointF p;
|
|
|
8cb222 |
if (Geometry.findIntersection(side.a, side.b, linkDesc.src, linkDesc.dst, out p)) {
|
|
|
8cb222 |
link.visible = true;
|
|
|
8cb222 |
linkDesc.src = p;
|
|
|
8cb222 |
side.links.Add(linkDesc);
|
|
|
8cb222 |
break;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
foreach(SideDesc side in sides) {
|
|
|
8cb222 |
side.links.Sort(delegate(LinkDesc a, LinkDesc b) {
|
|
|
8cb222 |
return Geometry.comparePointsAtLine(a.src, b.src, side.a, side.b); });
|
|
|
8cb222 |
for(int i = 0; i < side.links.Count; ++i) {
|
|
|
8cb222 |
ActiveLink link = side.links[i].link;
|
|
|
8cb222 |
PointF p = Geometry.pointAtLine(side.a, side.b, i, side.links.Count, padding);
|
|
|
8cb222 |
if (link.src == this) {
|
|
|
8cb222 |
link.srcBase = p;
|
|
|
8cb222 |
link.srcTangent = side.normal;
|
|
|
8cb222 |
link.pen = pen;
|
|
|
8cb222 |
} else {
|
|
|
8cb222 |
link.dstBase = p;
|
|
|
8cb222 |
link.dstTangent = new PointF(-side.normal.X, -side.normal.Y);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public class ActiveLink {
|
|
|
8cb222 |
public Link link;
|
|
|
8cb222 |
public ActiveBlock src;
|
|
|
8cb222 |
public ActiveBlock dst;
|
|
|
8cb222 |
|
|
|
8cb222 |
public SizeF arrowSize;
|
|
|
8cb222 |
public Pen pen;
|
|
|
8cb222 |
|
|
|
8cb222 |
public bool visible;
|
|
|
8cb222 |
public PointF srcBase;
|
|
|
8cb222 |
public PointF srcTangent;
|
|
|
8cb222 |
public PointF dstBase;
|
|
|
8cb222 |
public PointF dstTangent;
|
|
|
8cb222 |
|
|
|
8cb222 |
public PointF[] bezier;
|
|
|
8cb222 |
public PointF[] arrow;
|
|
|
8cb222 |
|
|
|
8cb222 |
public static readonly PointF[] arrowTemplate = new PointF[] {
|
|
|
8cb222 |
new PointF(0f, 0f),
|
|
|
8cb222 |
new PointF(1f, 1f),
|
|
|
8cb222 |
new PointF(0f, 0.75f) };
|
|
|
8cb222 |
|
|
|
8cb222 |
public static readonly float arrowOffset = 0.75f;
|
|
|
8cb222 |
|
|
|
8cb222 |
public static PointF[] makeArrow(PointF point, PointF tangent, SizeF size) {
|
|
|
8cb222 |
PointF[] arrow = new PointF[2*arrowTemplate.Length];
|
|
|
8cb222 |
PointF tx = new PointF( -0.5f*tangent.Y*size.Width, 0.5f*tangent.X*size.Width );
|
|
|
8cb222 |
PointF ty = new PointF( -tangent.X*size.Height, -tangent.Y*size.Height );
|
|
|
8cb222 |
for(int i = 0; i < arrowTemplate.Length; ++i) {
|
|
|
8cb222 |
arrow[i] = new PointF(
|
|
|
8cb222 |
point.X + tx.X*arrowTemplate[i].X + ty.X*arrowTemplate[i].Y,
|
|
|
8cb222 |
point.Y + tx.Y*arrowTemplate[i].X + ty.Y*arrowTemplate[i].Y );
|
|
|
8cb222 |
arrow[2*arrowTemplate.Length - i - 1] = new PointF(
|
|
|
8cb222 |
point.X - tx.X*arrowTemplate[i].X + ty.X*arrowTemplate[i].Y,
|
|
|
8cb222 |
point.Y - tx.Y*arrowTemplate[i].X + ty.Y*arrowTemplate[i].Y );
|
|
|
8cb222 |
}
|
|
|
8cb222 |
return arrow;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void place() {
|
|
|
8cb222 |
if (!visible) return;
|
|
|
8cb222 |
|
|
|
8cb222 |
float dx = 0.5f*Math.Abs(dstBase.X - srcBase.X);
|
|
|
8cb222 |
float dy = 0.5f*Math.Abs(dstBase.Y - srcBase.Y);
|
|
|
8cb222 |
float l = (float)Math.Sqrt(dx*dx + dy*dy);
|
|
|
8cb222 |
if (l < Geometry.precision) { visible = false; return; }
|
|
|
8cb222 |
dx = Math.Max(dx, 0.25f*l);
|
|
|
8cb222 |
dy = Math.Max(dy, 0.25f*l);
|
|
|
8cb222 |
|
|
|
8cb222 |
float sl = l;
|
|
|
8cb222 |
if (Math.Abs(srcTangent.X) > Geometry.precision)
|
|
|
8cb222 |
sl = Math.Min(sl, dx/Math.Abs(srcTangent.X));
|
|
|
8cb222 |
if (Math.Abs(srcTangent.Y) > Geometry.precision)
|
|
|
8cb222 |
sl = Math.Min(sl, dy/Math.Abs(srcTangent.Y));
|
|
|
8cb222 |
PointF st = new PointF(srcTangent.X*sl, srcTangent.Y*sl);
|
|
|
8cb222 |
|
|
|
8cb222 |
float dl = l;
|
|
|
8cb222 |
if (Math.Abs(dstTangent.X) > Geometry.precision)
|
|
|
8cb222 |
dl = Math.Min(dl, dx/Math.Abs(dstTangent.X));
|
|
|
8cb222 |
if (Math.Abs(dstTangent.Y) > Geometry.precision)
|
|
|
8cb222 |
dl = Math.Min(dl, dy/Math.Abs(dstTangent.Y));
|
|
|
8cb222 |
PointF dt = new PointF(dstTangent.X*sl, dstTangent.Y*sl);
|
|
|
8cb222 |
|
|
|
8cb222 |
bezier = new PointF[] {
|
|
|
8cb222 |
srcBase,
|
|
|
8cb222 |
new PointF(srcBase.X + st.X, srcBase.Y + st.Y),
|
|
|
8cb222 |
new PointF(dstBase.X - dt.X, dstBase.Y - dt.Y),
|
|
|
8cb222 |
new PointF(dstBase.X - dstTangent.X*(arrowOffset*arrowSize.Height + 0.5f*pen.Width),
|
|
|
8cb222 |
dstBase.Y - dstTangent.Y*(arrowOffset*arrowSize.Height + 0.5f*pen.Width))
|
|
|
8cb222 |
};
|
|
|
8cb222 |
|
|
|
8cb222 |
arrow = makeArrow(
|
|
|
8cb222 |
new PointF(dstBase.X - 0.5f*dstTangent.X*pen.Width,
|
|
|
8cb222 |
dstBase.Y - 0.5f*dstTangent.Y*pen.Width),
|
|
|
8cb222 |
dstTangent,
|
|
|
8cb222 |
arrowSize );
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void draw(Graphics g) {
|
|
|
8cb222 |
if (!visible) return;
|
|
|
8cb222 |
g.DrawBeziers(pen, bezier);
|
|
|
8cb222 |
g.FillPolygon(new SolidBrush(pen.Color), arrow);
|
|
|
8cb222 |
//g.DrawPolygon(pen, arrow);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public class ActiveDiagram {
|
|
|
8cb222 |
public Diagram diagram;
|
|
|
8cb222 |
|
|
|
8cb222 |
public readonly Dictionary<string, pointf=""> positions = new Dictionary<string, pointf="">();</string,></string,>
|
|
|
8cb222 |
|
|
|
8cb222 |
public Font captionFont;
|
|
|
8cb222 |
public Font textFont;
|
|
|
8cb222 |
public float margin;
|
|
|
8cb222 |
public float padding;
|
|
|
8cb222 |
public SizeF arrowSize;
|
|
|
8cb222 |
public Pen pen;
|
|
|
8cb222 |
public Brush brush;
|
|
|
8cb222 |
|
|
|
8cb222 |
public readonly Dictionary<string, activeblock=""> blocks = new Dictionary<string, activeblock="">();</string,></string,>
|
|
|
8cb222 |
public readonly Dictionary<string, activelink=""> links = new Dictionary<string, activelink="">();</string,></string,>
|
|
|
8cb222 |
|
|
|
8cb222 |
public RectangleF bounds;
|
|
|
8cb222 |
|
|
|
8cb222 |
public void savePositions(string filename) {
|
|
|
8cb222 |
remeberPositions();
|
|
|
8cb222 |
CultureInfo ci = new CultureInfo("en-US");
|
|
|
8cb222 |
List<string> lines = new List<string>();</string></string>
|
|
|
8cb222 |
foreach(KeyValuePair<string, pointf=""> pair in positions) {</string,>
|
|
|
8cb222 |
lines.Add(pair.Key);
|
|
|
8cb222 |
lines.Add(pair.Value.X.ToString(ci));
|
|
|
8cb222 |
lines.Add(pair.Value.Y.ToString(ci));
|
|
|
8cb222 |
}
|
|
|
8cb222 |
File.Create(filename).Close();
|
|
|
8cb222 |
File.WriteAllLines(filename, lines);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void loadPositions(string filename) {
|
|
|
8cb222 |
CultureInfo ci = new CultureInfo("en-US");
|
|
|
8cb222 |
string[] lines = File.ReadAllLines(filename);
|
|
|
8cb222 |
for(int i = 0; i < lines.Length - 2; i += 3) {
|
|
|
8cb222 |
string k = lines[i];
|
|
|
8cb222 |
PointF p = new PointF(
|
|
|
8cb222 |
float.Parse(lines[i+1], ci),
|
|
|
8cb222 |
float.Parse(lines[i+2], ci) );
|
|
|
8cb222 |
if (positions.ContainsKey(k))
|
|
|
8cb222 |
positions[k] = p;
|
|
|
8cb222 |
else
|
|
|
8cb222 |
positions.Add(k, p);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
restorePositions();
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void remeberPositions() {
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
if (positions.ContainsKey(pair.Key))
|
|
|
8cb222 |
positions[pair.Key] = pair.Value.position;
|
|
|
8cb222 |
else
|
|
|
8cb222 |
positions.Add(pair.Key, pair.Value.position);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void restorePositions() {
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
if (positions.ContainsKey(pair.Key))
|
|
|
8cb222 |
pair.Value.position = positions[pair.Key];
|
|
|
8cb222 |
placeLinks();
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
// insert/remove blocks and links calculate sizes
|
|
|
8cb222 |
public void reloadDiagram() {
|
|
|
8cb222 |
remeberPositions();
|
|
|
8cb222 |
|
|
|
8cb222 |
bool retry;
|
|
|
8cb222 |
|
|
|
8cb222 |
// blocks
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
pair.Value.block = null;
|
|
|
8cb222 |
foreach(KeyValuePair<string, block=""> pair in diagram.blocks) {</string,>
|
|
|
8cb222 |
if (!blocks.ContainsKey(pair.Key))
|
|
|
8cb222 |
blocks.Add(pair.Key, new ActiveBlock());
|
|
|
8cb222 |
ActiveBlock b = blocks[pair.Key];
|
|
|
8cb222 |
b.block = pair.Value;
|
|
|
8cb222 |
b.links.Clear();
|
|
|
8cb222 |
b.captionFont = captionFont;
|
|
|
8cb222 |
b.textFont = textFont;
|
|
|
8cb222 |
b.margin = margin;
|
|
|
8cb222 |
b.padding = padding;
|
|
|
8cb222 |
b.pen = new Pen(new SolidBrush(b.block.color), pen.Width);
|
|
|
8cb222 |
b.brush = brush;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
retry = true;
|
|
|
8cb222 |
while(retry) {
|
|
|
8cb222 |
retry = false;
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
if (pair.Value.block == null)
|
|
|
8cb222 |
{ blocks.Remove(pair.Key); retry = true; break; }
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
// links
|
|
|
8cb222 |
foreach(KeyValuePair<string, activelink=""> pair in links)</string,>
|
|
|
8cb222 |
pair.Value.link = null;
|
|
|
8cb222 |
foreach(KeyValuePair<string, link=""> pair in diagram.links) {</string,>
|
|
|
8cb222 |
if ( blocks.ContainsKey(pair.Value.srcId)
|
|
|
8cb222 |
&& blocks.ContainsKey(pair.Value.dstId) )
|
|
|
8cb222 |
{
|
|
|
8cb222 |
if (!links.ContainsKey(pair.Key))
|
|
|
8cb222 |
links.Add(pair.Key, new ActiveLink());
|
|
|
8cb222 |
ActiveLink l = links[pair.Key];
|
|
|
8cb222 |
l.link = pair.Value;
|
|
|
8cb222 |
l.src = blocks[l.link.srcId];
|
|
|
8cb222 |
l.src.links.Add(l);
|
|
|
8cb222 |
l.dst = blocks[l.link.dstId];
|
|
|
8cb222 |
l.dst.links.Add(l);
|
|
|
8cb222 |
l.arrowSize = arrowSize;
|
|
|
8cb222 |
l.pen = pen;
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
retry = true;
|
|
|
8cb222 |
while(retry) {
|
|
|
8cb222 |
retry = false;
|
|
|
8cb222 |
foreach(KeyValuePair<string, activelink=""> pair in links)</string,>
|
|
|
8cb222 |
if (pair.Value.link == null)
|
|
|
8cb222 |
{ links.Remove(pair.Key); retry = true; break; }
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
measureBlocks();
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void measureBlocks() {
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
pair.Value.measure();
|
|
|
8cb222 |
placeLinks();
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void placeLinks() {
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
pair.Value.placeLinks();
|
|
|
8cb222 |
foreach(KeyValuePair<string, activelink=""> pair in links)</string,>
|
|
|
8cb222 |
pair.Value.place();
|
|
|
8cb222 |
recalcBounds();
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void recalcBounds() {
|
|
|
8cb222 |
if (blocks.Count == 0) { bounds = new RectangleF(); return; }
|
|
|
8cb222 |
float minx = float.PositiveInfinity;
|
|
|
8cb222 |
float miny = float.PositiveInfinity;
|
|
|
8cb222 |
float maxx = float.NegativeInfinity;
|
|
|
8cb222 |
float maxy = float.NegativeInfinity;
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks) {</string,>
|
|
|
8cb222 |
minx = Math.Min(minx, pair.Value.position.X - pair.Value.margin);
|
|
|
8cb222 |
miny = Math.Min(miny, pair.Value.position.Y - pair.Value.margin);
|
|
|
8cb222 |
maxx = Math.Max(maxx, pair.Value.position.X + pair.Value.size.Width + pair.Value.margin);
|
|
|
8cb222 |
maxy = Math.Max(maxy, pair.Value.position.Y + pair.Value.size.Height + pair.Value.margin);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
bounds = new RectangleF(minx, miny, maxx - minx, maxy - miny);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|
|
|
8cb222 |
public void draw(Graphics g) {
|
|
|
8cb222 |
foreach(KeyValuePair<string, activelink=""> pair in links)</string,>
|
|
|
8cb222 |
pair.Value.draw(g);
|
|
|
8cb222 |
foreach(KeyValuePair<string, activeblock=""> pair in blocks)</string,>
|
|
|
8cb222 |
pair.Value.draw(g);
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
}
|
|
|
8cb222 |
|