Blame mono/Diagram/ActiveDiagram.cs

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