Пример #1
0
		private ArrowCrossings getCrossings()
		{
			float crad = flowChart.CrossingRadius;
			RectangleF rcArrow = getBoundingRect();
			PointCollection intersections = new PointCollection(0);

			ArrowCrossings ac = (ArrowCrossings)getData(Constants.ARROW_CROSSINGS);
			if (ac == null)
			{
				ac = new ArrowCrossings(segmentCount);
				setData(Constants.ARROW_CROSSINGS, ac);

				// z index where this arrow is or will be placed
				int z = ZIndex;
				if (!constructed)
					z = flowChart.Objects.Count;

				// get the arrows which should be checked for intersections
				ArrowCollection arrows;
				if (flowChart.ArrowCrossings ==
					MindFusion.FlowChartX.ArrowCrossings.Arcs)
					arrows = flowChart.getArrowsFromZ(true, z);
				else
					arrows = flowChart.getArrowsFromZ(false, z);

				// check each segment
				for (int sgt = 0; sgt < segmentCount; ++sgt)
				{
					PointF pt1 = Points[sgt];
					PointF pt2 = Points[sgt+1];
					if (pt1 == pt2) continue;

					// find intersecting points with each arrow
					for (int i = 0; i < arrows.Count; ++i)
					{
						Arrow arrow = arrows[i];
						if (!arrow.Visible || arrow.Style == ArrowStyle.Bezier)
							continue;

						// dont check segments if arrow bounding rects dont intersect at all
						RectangleF rcTest = arrow.getBoundingRect();
						if (!rcTest.IntersectsWith(rcArrow))
							continue;

						for (int test = 0; test < arrow.SegmentCount; ++test)
						{
							PointF testPt1 = arrow.Points[test];
							PointF testPt2 = arrow.Points[test+1];
							if (testPt1 == testPt2) continue;

							PointF intersection = new PointF(0, 0);
							if (Utilities.segmentIntersect(pt1, pt2,
								testPt1, testPt2, ref intersection))
							{
								if (Utilities.Distance(pt1, intersection) > crad &&
									Utilities.Distance(pt2, intersection) > crad)
									intersections.Add(intersection);
							}
						}
					}

					// sort by distance to the first point
					intersections.Sort(new CloserDistance(pt1));

					// add radial intersections to the runtime intersection data
					CloserDistance closer = new CloserDistance(pt1);
					PointCollection rintr = ac.segmentCrossings[sgt] as PointCollection;
					rintr.Add(pt1);
					for (int ptc = 0; ptc < intersections.Count; ++ptc)
					{
						PointF ptRes1, ptRes2, pt = intersections[ptc];
						ptRes1 = ptRes2 = pt;
						RectangleF rc = RectangleF.FromLTRB(
							pt.X - crad, pt.Y - crad, pt.X + crad, pt.Y + crad);
						Utilities.getEllipseIntr(rc, pt1, pt, ref ptRes1);
						Utilities.getEllipseIntr(rc, pt2, pt, ref ptRes2);
						if (closer.Compare(ptRes1, ptRes2) < 0)
						{
							rintr.Add(ptRes1);
							rintr.Add(ptRes2);
						}
						else
						{
							rintr.Add(ptRes2);
							rintr.Add(ptRes1);
						}
					}
					rintr.Add(pt2);

					// Check if there are intersection that overlap
					for (int i = 1; i < rintr.Count - 2; )
					{
						PointF p1 = rintr[i];
						PointF p2 = rintr[i + 1];

						if (closer.Compare(p1, p2) > 0 ||
							Utilities.Distance(p1, p2) < Constants.getMillimeter(flowChart.MeasureUnit) / 2)
						{
							// Remove these points
							rintr.RemoveAt(i);
							rintr.RemoveAt(i);
						}
						else
						{
							i++;
						}
					}

					intersections.Clear();

				}	// for (int sgt = 0; sgt < GetSegments(); ++sgt)
			}

			return ac;
		}
Пример #2
0
		public void Read(System.Xml.XmlReader reader)
		{
			// Disable auto-routing for a while
			_diagram.DontRouteForAwhile = true;

			_diagram.ClearAll();

			// Read while <Diagram> reached
			while (reader.Read())
				if (reader.Name == "Diagram")
					break;

			if (reader.Name != "Diagram")
				throw new InvalidFormatException("Invalid FlowChart document");

			// Check version
			_version = 1;
			if (reader.HasAttributes)
				_version = XmlConvert.ToInt32(reader.GetAttribute("Version"));

			// Read the brushes
			SortedList brushes = new SortedList();
			Brush brush;
			int count;
			int i;
			int id = 0;
			string type;
			int d;

			ReadMandatory(reader, "Brushes",
				"Brushes element missing or invalid");
			count = XmlConvert.ToInt32(
				reader.GetAttribute("Count"));
			for(i = 0; i < count; i++)
			{
				ReadMandatory(reader, "Brush",
					"Unrecognized brush token");

				id = XmlConvert.ToInt32(
					reader.GetAttribute("Id"));
				type = reader.GetAttribute("Type");

				brush = null;

				switch(type)
				{
					case "Solid":
					{
						d = reader.Depth;
						string name;
						while(true)
						{
							// Read element
							reader.Read();
							if(reader.Depth == d)
								break;

							name = reader.Name;
							// Read the value
							reader.Read();

							if(name == "Color")
							{
								Color c = XmlConvert.ToColor(reader.Value);
								brush = new FlowChartX.SolidBrush(c);
							}

							// Read the closing element
							reader.Read();
						}
					}
						break;
					case "Gradient":
					{
						d = reader.Depth;
						float angle = 0;
						Blend blend = null;
						Color c1 = Color.Black, c2 = Color.White;
						ColorBlend cblend = null;
						while(true)
						{
							// Read element
							reader.Read();
							if(reader.Depth == d)
								break;

							switch (reader.Name)
							{
								case "Angle":
									// Read the value
									reader.Read();
									angle = XmlConvert.ToSingle(reader.Value);
									break;
								case "Blend":
								{
									// Read positions
									reader.Read();
									float[] pos = ReadFloatArrayElement(reader);
									// Read factors
									reader.Read();
									float[] fac = ReadFloatArrayElement(reader);

									if(pos.Length != fac.Length)
										throw new InvalidFormatException(
											"Factors and positions in a gradient brush " +
											"have different lengths");

									blend = new Blend();
									blend.Positions = pos;
									blend.Factors = fac;
								}
									break;
								case "Color1":
									// Read the value
									reader.Read();
									c1 = XmlConvert.ToColor(reader.Value);
									break;
								case "Color2":
									// Read the value
									reader.Read();
									c2 = XmlConvert.ToColor(reader.Value);
									break;
								case "ColorBlend":
								{
									// Read positions
									reader.Read();
									float[] pos = ReadFloatArrayElement(reader);
									// Read colors
									reader.Read();
									Color[] colors = ReadColorArrayElement(reader);

									cblend = new ColorBlend();
									cblend.Positions = pos;
									cblend.Colors = colors;
								}
									break;
							}

							// Read the closing element
							reader.Read();
						}

						brush = new FlowChartX.LinearGradientBrush(c1, c2);
						((LinearGradientBrush)brush).Angle = angle;
						((LinearGradientBrush)brush).Blend = blend;
						((LinearGradientBrush)brush).InterpolationColor = cblend;
					}
						break;
					case "Texture":
					{
						d = reader.Depth;
						string name;
						WrapMode wm = WrapMode.Tile;
						Image img = null;
						while(true)
						{
							// Read element
							reader.Read();
							if(reader.Depth == d)
								break;

							name = reader.Name;
							// Read the value
							reader.Read();

							switch(name)
							{
								case "WrapMode":
									wm = (WrapMode)XmlConvert.ToEnum(
										typeof(WrapMode), reader.Value);
									break;
								case "Image":
									img = _version >= 4 ? XmlConvert.ToImageV4(reader.Value) :
										XmlConvert.ToImage(reader.Value);
									break;
							}

							// Read the closing element
							reader.Read();

							if (img != null)
								brush = new FlowChartX.TextureBrush(img, wm);
						}
					}
						break;
					case "Hatch":
					{
						d = reader.Depth;
						string name;
						Color fore = Color.Black, back = Color.White;
						HatchStyle style = HatchStyle.ForwardDiagonal;
						while(true)
						{
							// Read element
							reader.Read();
							if(reader.Depth == d)
								break;

							name = reader.Name;
							// Read the value
							reader.Read();

							switch(name)
							{
								case "ForeColor":
									fore = XmlConvert.ToColor(reader.Value);
									break;
								case "BackColor":
									back = XmlConvert.ToColor(reader.Value);
									break;
								case "Style":
									style = (HatchStyle)XmlConvert.ToEnum(
										typeof(HatchStyle), reader.Value);
									break;
							}

							// Read the closing element
							reader.Read();

							brush = new FlowChartX.HatchBrush(
								style, fore, back);
						}
					}
						break;
				}

				if(!brushes.Contains(id) && brush != null)
					brushes.Add(id, brush);
			}

			// Read the brushes closing element
			if(count > 0)
				reader.Read();

			ReadMandatory(reader, "Environment",
				"Environment element missing or invalid");

			// Read all appearance properties
			ReadMandatory(reader, "Appearance",
				"Appearance element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Appearance section");

			// Read all bahaviour properties
			ReadMandatory(reader, "Behaviour",
				"Behaviour element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Behaviour section");

			// Read all default properties
			ReadMandatory(reader, "Defaults",
				"Defaults element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Defaults section");

			// Read default brushes and pens
			ReadMandatory(reader, "DefaultsGDI",
				"DefaultsGDI element missing or invalid");
			d = reader.Depth;
			while(true)
			{
				// Read starting tag
				reader.Read();
				if(reader.Depth == d)
					break;

				switch (reader.Name)
				{

					case "BoxBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						_diagram.BoxBrush = (FlowChartX.Brush)brushes[id];
						break;

					case "TableBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						_diagram.TableBrush = (FlowChartX.Brush)brushes[id];
						break;
/*
					case "TableCaptionBackBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						_diagram.TableCaptionBackBrush = (FlowChartX.Brush)brushes[id];
						break;
*/
					case "ArrowBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						_diagram.ArrowBrush = (FlowChartX.Brush)brushes[id];
						break;

					case "BackBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						_diagram.BackBrush = (FlowChartX.Brush)brushes[id];
						break;

					case "ExteriorBrush":
						id = XmlConvert.ToInt32(
							reader.GetAttribute("Id"));
						if (id == -1)
							_diagram.ExteriorBrush = null;
						else
							_diagram.ExteriorBrush = (FlowChartX.Brush)brushes[id];
						break;

					case "BoxPen":
						_diagram.BoxPen.Brush = null; // force release
						_diagram.BoxPen = (FlowChartX.Pen)
							ReadPenElement(reader, brushes);
						break;

					case "TablePen":
						_diagram.TablePen.Brush = null; // force release
						_diagram.TablePen = (FlowChartX.Pen)
							ReadPenElement(reader, brushes);
						break;

					case "ArrowPen":
						_diagram.ArrowPen.Brush = null; // force release
						_diagram.ArrowPen = (FlowChartX.Pen)
							ReadPenElement(reader, brushes);
						break;

				}
			}

			// Read all grid properties
			ReadMandatory(reader, "Grid",
				"Grid element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Grid section");

			// Read all layout properties
			ReadMandatory(reader, "Layout",
				"Layout element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Layout section");

			// Read all miscellaneous properties
			ReadMandatory(reader, "Miscellaneous",
				"Miscellaneous element missing or invalid");
			if(!ReadProperties(reader, _diagram, reader.Depth + 1))
				throw new InvalidFormatException("Unexpected " +
					"EOF in the Miscellaneous section");

			// Read the Environment closing element
			reader.Read();


			// Proceed to diagram objects //
			Box b;
			ControlHost h;
			Arrow a;
			Table t;
			Group g;
			object from, to;
			PointCollection points = new PointCollection(0);
			int idFrom, idTo, idArrow;
			int rowFrom, rowTo;
			int row, col;
			SortedList objects = new SortedList();
			SortedList zOrder = new SortedList();
			SortedList controlZOrder = new SortedList();
			int zIndex;
			id = 0;

			// Read boxes info
			ReadMandatory(reader, "Boxes",
				"Boxes element missing or invalid");
			count = XmlConvert.ToInt32(reader.GetAttribute("Count"));
			int brushId = -1;
			FlowChartX.Pen pen = null;
			FlowChartX.Pen headPen = null;
			for(i = 0; i < count; i++)
			{
				// Read the starting element
				ReadMandatory(reader, "Box",
					"Unrecognized box token");

				id = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				zIndex = XmlConvert.ToInt32(reader.GetAttribute("ZIndex"));
				b = _diagram.CreateBox(0, 0, 1, 1);
				objects.Add(id, b);
				zOrder.Add(zIndex, b);

				// Read the shape
				string shape;
				reader.Read();
				if(!reader.IsEmptyElement)
				{
					reader.Read();
					shape = reader.Value;
					reader.Read();
				}
				else
				{
					shape = "Rectangle";
				}

				BoxStyle style = BoxStyle.Rectangle;
				switch(shape)
				{
					case "RoundRectangle":
						style = BoxStyle.RoundedRectangle;
						break;
					default:
						// Assume it is a complex shape
						style = BoxStyle.Shape;
						break;
				}
				b.Style = style;
				if(style == BoxStyle.Shape)
					b.Shape = ShapeTemplate.FromId(shape);

				// Read brush
				reader.Read();
				if (reader.GetAttribute("Id") != null)
					brushId = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				else
					brushId = -1;
				// Read the pen
				reader.Read();
				pen = ReadPenElement(reader, brushes);

				if(!ReadProperties(reader, b, reader.Depth))
					throw new InvalidFormatException(
						"Unexpected EOF while parsing box info. Box: " +
						i.ToString());

				// Set brush and pen
				if(brushId != -1)
					b.Brush = (FlowChartX.Brush)brushes[brushId];
				if(pen != null)
					b.Pen = pen;
			}

			// Read boxes closing element
			if(count > 0)
				reader.Read();

			// Read control hosts
			if (_version >= 3)
			{
				string assemblyName;
				string typeName;
				ReadMandatory(reader, "ControlHosts",
					"ControlHosts element missing or invalid");
				count = XmlConvert.ToInt32(reader.GetAttribute("Count"));

				for (i = 0; i < count; i++)
				{
					// Read the host element
					ReadMandatory(reader, "Host",
						"Host element missing or invalid");

					id = XmlConvert.ToInt32(reader.GetAttribute("Id"));
					zIndex = XmlConvert.ToInt32(reader.GetAttribute("ZIndex"));
					assemblyName = reader.GetAttribute("ControlAssembly");
					typeName = reader.GetAttribute("ControlType");
					int controlZIndex = XmlConvert.ToInt32(reader.GetAttribute("ControlZIndex"));

					// Create the control host and add it to the diagram
					h = _diagram.CreateControlHost(0, 0, 1, 1);
					objects.Add(id, h);
					zOrder.Add(zIndex, h);
					controlZOrder.Add(controlZIndex, h.Control);

					// Read brush
					reader.Read();
					if(reader.GetAttribute("Id") != null)
						brushId = XmlConvert.ToInt32(reader.GetAttribute("Id"));
					else
						brushId = -1;
					// Read the pen
					reader.Read();
					pen = ReadPenElement(reader, brushes);

					if (typeName != "" && assemblyName != "")
					{
						System.Type controlType = Utilities.getLoadedType(typeName, assemblyName);
						if (controlType == null)
							throw new FileLoadException("Cannot load hosted control of type " + typeName);
						ConstructorInfo ctorInfo = controlType.GetConstructor(System.Type.EmptyTypes);
						// Instantiate
						h.Control = (System.Windows.Forms.Control)ctorInfo.Invoke(null);

						// Read control properties
						BinaryFormatter fmt = new BinaryFormatter();
						fmt.Binder = new DeserializationHack();
						ReadControlProperties(reader, h.Control, fmt);
					}
					else
					{
						h.Control = null;
					}

					// Read properties
					if (!ReadProperties(reader, h, reader.Depth))
						throw new InvalidFormatException(
							"Unexpected EOF while parsing control host info. ControlHost: " +
							i.ToString());

					// Set brush and pen
					if (brushId != -1)
						h.Brush = (FlowChartX.Brush)brushes[brushId];
					if (pen != null)
						h.Pen = pen;
				}

				// Update the z-indices of the contained controls
				foreach (System.Windows.Forms.Control control in controlZOrder.Values)
					control.BringToFront();

				// Read control hosts closing element
				if(count > 0)
					reader.Read();
			}

			// Read tables info
			ReadMandatory(reader, "Tables",
				"Tables element missing or invalid");
			count = XmlConvert.ToInt32(reader.GetAttribute("Count"));
			for(i = 0; i < count; i++)
			{
				// Read the table element
				ReadMandatory(reader, "Table",
					"Unrecognized table token");

				id = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				zIndex = XmlConvert.ToInt32(reader.GetAttribute("ZIndex"));
				t = _diagram.CreateTable(0, 0, 1, 1);
				objects.Add(id, t);
				zOrder.Add(zIndex, t);

				t.RowCount = XmlConvert.ToInt32(reader.GetAttribute("Rows"));
				t.ColumnCount = XmlConvert.ToInt32(reader.GetAttribute("Columns"));

				// Read cell data
				ReadMandatory(reader, "Data",
					"Data element missing or invalid");
				d = reader.Depth;
				row = 0;
				col = 0;
				if(!reader.IsEmptyElement)
				{
					while(true)
					{
						reader.Read();
						if(d == reader.Depth)
							break;

						// Read brush in V5
						if (_version >= 5)
						{
							reader.Read();
							int bid = -1;
							if (reader.GetAttribute("Id") != null)
								bid = XmlConvert.ToInt32(reader.GetAttribute("Id"));
							if (bid != -1)
								t[col, row].Brush = (FlowChartX.Brush)brushes[bid];
							else
								t[col, row].Brush = null;

							ReadProperties(reader, t[col, row], reader.Depth);
						}
						else
						{
							ReadProperties(reader, t[col, row], reader.Depth + 1);
						}

						col++;
						if(col >= t.ColumnCount)
						{
							col = 0;
							row++;
						}
					}
				}

				// Read row data
				ReadMandatory(reader, "Rows",
					"Rows element missing or invalid");
				d = reader.Depth;
				row = 0;
				if(!reader.IsEmptyElement)
				{
					while(true)
					{
						reader.Read();
						if(d == reader.Depth)
							break;

						ReadProperties(reader, t.Rows[row], reader.Depth + 1);
						row++;
					}
				}

				// Read column data
				ReadMandatory(reader, "Columns",
					"Columns element missing or invalid");
				d = reader.Depth;
				col = 0;
				if(!reader.IsEmptyElement)
				{
					while(true)
					{
						reader.Read();
						if(d == reader.Depth)
							break;

						ReadProperties(reader, t.Columns[col], reader.Depth + 1);
						col++;
					}
				}

				// Read brush
				reader.Read();
				if (reader.GetAttribute("Id") != null)
					brushId = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				else
					brushId = -1;

				// Read the caption brush
				int captionBrushId = -1;
				if (_version >= 10)
				{
					reader.Read();
					if (reader.GetAttribute("Id") != null)
						captionBrushId = XmlConvert.ToInt32(reader.GetAttribute("Id"));
					else
						captionBrushId = -1;
				}

				// Read the pen
				reader.Read();
				pen = ReadPenElement(reader, brushes);

				// Read table properties
				if(!ReadProperties(reader, t, reader.Depth))
					throw new InvalidFormatException(
						"Unexpected EOF while parsing table info. Table: " +
						i.ToString());

				// Set brush and pen
				if (brushId != -1)
					t.Brush = (FlowChartX.Brush)brushes[brushId];
				if (captionBrushId != -1)
					t.CaptionBackBrush = (FlowChartX.Brush)brushes[captionBrushId];
				if (pen != null)
					t.Pen = pen;
			}

			// Read tables closing element
			if(count > 0)
				reader.Read();

			if (_version >= 7)
			{
				ReadMandatory(reader, "Containers",
					"Containers element missing or invalid");

				int ccount = XmlConvert.ToInt32(reader.GetAttribute("Count"));

				if (ccount > 0)
				{
					int cdepth = reader.Depth;
					while (true)
					{
						reader.Read();
						if (reader.Depth == cdepth)
							break;
					}
				}
			}

			// Read arrows info
			ReadMandatory(reader, "Arrows",
				"Arrows element missing or invalid");
			count = XmlConvert.ToInt32(reader.GetAttribute("Count"));
			for(i = 0; i < count; i++)
			{
				// Read the arrow element
				ReadMandatory(reader, "Arrow",
					"Unrecognized arrow token");

				// Read the origin and destination indices
				idFrom = XmlConvert.ToInt32(reader.GetAttribute("From"));
				idTo = XmlConvert.ToInt32(reader.GetAttribute("To"));
				idArrow = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				zIndex = XmlConvert.ToInt32(reader.GetAttribute("ZIndex"));

				try
				{
					rowFrom = XmlConvert.ToInt32(reader.GetAttribute("RowFrom"));
				}
				catch
				{
					rowFrom = -1;
				}
				try
				{
					rowTo = XmlConvert.ToInt32(reader.GetAttribute("RowTo"));
				}
				catch
				{
					rowTo = -1;
				}

				if (idTo == -1 || idFrom == -1)
				{
					if (idTo == -1 && idFrom != -1)
					{
						from = objects[idFrom];

						Node nodeFrom = from as Node;

						// Temporarily turn allow arrows off
						bool allowIn = nodeFrom.AllowIncomingArrows;
						bool allowOut = nodeFrom.AllowOutgoingArrows;

						nodeFrom.AllowIncomingArrows = true;
						nodeFrom.AllowOutgoingArrows = true;

						a = _diagram.CreateArrow(nodeFrom, PointF.Empty);

						nodeFrom.AllowIncomingArrows = allowIn;
						nodeFrom.AllowOutgoingArrows = allowOut;
					}
					else if (idTo != -1 && idFrom == -1)
					{
						to = objects[idTo];

						Node nodeTo = to as Node;

						// Temporarily turn allow arrows off
						bool allowIn = nodeTo.AllowIncomingArrows;
						bool allowOut = nodeTo.AllowOutgoingArrows;

						nodeTo.AllowIncomingArrows = true;
						nodeTo.AllowOutgoingArrows = true;

						a = _diagram.CreateArrow(PointF.Empty, (Node)to);

						nodeTo.AllowIncomingArrows = allowIn;
						nodeTo.AllowOutgoingArrows = allowOut;
					}
					else
					{
						a = _diagram.CreateArrow(PointF.Empty, PointF.Empty);
					}
				}
				else
				{
					from = objects[idFrom];
					to = objects[idTo];

					Node nodeFrom = from as Node;
					Node nodeTo = to as Node;

					// Temporarily turn allow arrows off
					bool fromAllowIn = nodeFrom.AllowIncomingArrows;
					bool fromAllowOut = nodeFrom.AllowOutgoingArrows;
					bool toAllowIn = nodeTo.AllowIncomingArrows;
					bool toAllowOut = nodeTo.AllowOutgoingArrows;

					nodeFrom.AllowIncomingArrows = true;
					nodeFrom.AllowOutgoingArrows = true;
					nodeTo.AllowIncomingArrows = true;
					nodeTo.AllowOutgoingArrows = true;

					if(rowFrom == -1 && rowTo == -1)
					{
						a = _diagram.CreateArrow((Node)from, (Node)to);
					}
					else if(rowFrom != -1 && rowTo == -1)
					{
						a = _diagram.CreateArrow((Table)from, rowFrom, (Node)to);
					}
					else if(rowFrom == -1 && rowTo != -1)
					{
						a = _diagram.CreateArrow((Node)from, (Table)to, rowTo);
					}
					else
					{
						a = _diagram.CreateArrow((Table)from, rowFrom,
							(Table)to, rowTo);
					}

					nodeFrom.AllowIncomingArrows = fromAllowIn;
					nodeFrom.AllowOutgoingArrows = fromAllowOut;
					nodeTo.AllowIncomingArrows = toAllowIn;
					nodeTo.AllowOutgoingArrows = toAllowOut;
				}

				// Read the control points
				ReadMandatory(reader, "Data",
					"Data element missing or invalid");
				d = reader.Depth;
				float x, y;
				points.Clear();
				while(true)
				{
					// Read the point
					reader.Read();

					if(reader.Depth == d)
						break;

					x = XmlConvert.ToSingle(reader.GetAttribute("X"));
					y = XmlConvert.ToSingle(reader.GetAttribute("Y"));

					points.Add(new PointF(x, y));
				}

				// Read brush
				reader.Read();
				if(reader.GetAttribute("Id") != null)
					brushId = XmlConvert.ToInt32(reader.GetAttribute("Id"));
				else
					brushId = -1;
				// Read the pen
				reader.Read();
				pen = ReadPenElement(reader, brushes);
				// Read head pen
				if (_version > 1)
				{
					reader.Read();
					headPen = ReadPenElement(reader, brushes);
				}

				// Read the properties
				if(!ReadProperties(reader, a, reader.Depth))
					throw new InvalidFormatException(
						"Unexpected EOF while parsing arrow info. Arrow: " +
						i.ToString());

				// Set again segment count, because
				// if the arrow is routed, settings
				// segment count through SegmentCount property
				// won't have any effect
				if (a.Style == ArrowStyle.Bezier)
					a.InternalSegmentCount = (short)((points.Count - 1) / 3);
				else
					a.InternalSegmentCount = (short)(points.Count - 1);

				// Set the control points
				for(int p = 0; p < points.Count; p++)
					a.ControlPoints[p] = points[p];
				a.UpdateFromPoints();

				// Set brush and pen
				if(brushId != -1)
					a.Brush = (FlowChartX.Brush)brushes[brushId];
				if(pen != null)
					a.Pen = pen;
				if (headPen != null)
					a.HeadPen = headPen;

				objects.Add(idArrow, a);
				zOrder.Add(zIndex, a);
			}

			// Read arrows closing element
			if(count > 0)
				reader.Read();


			// Adjust z-order
			for(i = 0; i < zOrder.Count; i++)
			{
				ChartObject obj = (ChartObject)zOrder.GetByIndex(i);
				obj.ZIndex = (int)zOrder.GetKey(i);
			}


			// Read groups
			ReadMandatory(reader, "Groups",
				"Groups element missing or invalid");
			count = XmlConvert.ToInt32(reader.GetAttribute("Count"));
			for(i = 0; i < count; i++)
			{
				// Read the group element
				ReadMandatory(reader, "Group",
					"Unrecognized group token");

				// Read the main object
				reader.Read();
				id = XmlConvert.ToInt32(reader.GetAttribute("Id"));

				g = _diagram.CreateGroup((ChartObject)objects[id]);

				// Read group's visibility
				reader.Read();
				reader.Read();
				g.Visible = XmlConvert.ToBoolean(reader.Value.ToLower());
				reader.Read();

				// Read AutoDeleteItems flag
				if (_version >= 7)
				{
					reader.Read();
					reader.Read();
					g.AutoDeleteItems = XmlConvert.ToBoolean(reader.Value.ToLower());
					reader.Read();
				}

				// Read Expandable flag
				if (_version >= 8)
				{
					reader.Read();
					reader.Read();
					g.Expandable = XmlConvert.ToBoolean(reader.Value.ToLower());
					reader.Read();
				}

				// Read FollowMasterRotation flag
				if (_version >= 9)
				{
					reader.Read();
					reader.Read();
					g.FollowMasterRotation = XmlConvert.ToBoolean(reader.Value.ToLower());
					reader.Read();
				}

				reader.Read(); // read Tag or Attachments
				if (reader.Name == "Tag")
				{
					if (_options.CustomTagSerialization)
					{
						if(DeserializeTag != null)
						{
							SerializeTagArgs args = new SerializeTagArgs(g, null, reader);
							DeserializeTag(this, args);
						}
					}
					else
					{
						// Read the value
						reader.Read();

						if(DeserializeTag != null)
						{
							SerializeTagArgs args = new SerializeTagArgs(g);
							args.Representation = reader.Value;
							DeserializeTag(this, args);
						}
					}

					// Read the closing Tag element
					reader.Read();

					// Read the Attachments
					reader.Read();
				}

				// Process attachments
				int acount = XmlConvert.ToInt32(reader.GetAttribute("Count"));
				int ai;
				int adata;
				Node node;
				float al, at, ar, ab;
				AttachTo atype;
				for(ai = 0; ai < acount; ai++)
				{
					// Read attachment element
					reader.Read();

					// Read data
					reader.Read();
					reader.Read();
					adata = XmlConvert.ToInt32(reader.Value);
					reader.Read();

					// Read object
					reader.Read();
					reader.Read();
					node = (Node)objects[XmlConvert.ToInt32(reader.Value)];
					reader.Read();

					// Read percents
					reader.Read();
					al = XmlConvert.ToSingle(reader.GetAttribute("Left"));
					at = XmlConvert.ToSingle(reader.GetAttribute("Top"));
					ar = XmlConvert.ToSingle(reader.GetAttribute("Right"));
					ab = XmlConvert.ToSingle(reader.GetAttribute("Bottom"));

					// Read type
					reader.Read();
					reader.Read();
					atype = (AttachTo)XmlConvert.ToEnum(
						typeof(AttachTo), reader.Value);
					reader.Read();

					switch(atype)
					{
						case AttachTo.ArrowPoint:
							g.AttachToArrowPoint(node, adata);
							break;
						case AttachTo.ArrowSegment:
							g.AttachToArrowSegment(node, adata);
							break;
						case AttachTo.FixedCorner:
							g.AttachToCorner(node, adata);
							break;
						case AttachTo.Proportional:
							g.AttachProportional(node, al, at, ar, ab);
							break;
						case AttachTo.LongestHSegment:
							g.AttachToLongestHSegment(node);
							break;
						case AttachTo.SideMiddle:
							g.AttachToSideMiddle(node, adata);
							break;
					}

					// Read attachment closing element
					reader.Read();
				}

				// Read attachments' closing element
				if(acount > 0)
					reader.Read();

				// Read the group closing element
				reader.Read();
			}

			// Read groups closing element
			reader.Read();

			// Read diagram closing element
			reader.Read();

			foreach (ChartObject obj in _diagram.Objects)
				obj.onLoad();

			// Note: If exception is thrown this
			// flag will remain raised
			_diagram.DontRouteForAwhile = false;
		}
Пример #3
0
		internal void doRoute(bool force, Link orgnLink, Link destLink, bool nowCreating)
		{
			if (!force)
				if (!autoRoute) return;

			if (flowChart.DontRouteForAwhile)
				return;

			int i;

			float gridSize = flowChart.RoutingOptions.GridSize;

			PointF startPoint = points[0];
			PointF endPoint = points[points.Count - 1];

			// get a rectangle bounding both the origin and the destination
			RectangleF bounds = orgnLink.getNodeRect(true);
			bounds = Utilities.unionRects(bounds, destLink.getNodeRect(true));
			bounds = RectangleF.Union(bounds, Utilities.normalizeRect(
				RectangleF.FromLTRB(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y)));
			if (bounds.Width < gridSize * 4)
				bounds.Inflate(gridSize * 4, 0);
			if (bounds.Height < gridSize * 4)
				bounds.Inflate(0, gridSize * 4);
			bounds.Inflate(bounds.Width, bounds.Height);

			int oNearest = 0, dNearest = 0;
			routeGetEndPoints(ref startPoint, ref endPoint,
				ref oNearest, ref dNearest, orgnLink, destLink, nowCreating);

			// Get the starting and ending square
			Point ptStart = new Point((int)((startPoint.X - bounds.X) / gridSize),
				(int)((startPoint.Y - bounds.Y) / gridSize));
			Point ptEnd = new Point((int)((endPoint.X - bounds.X) / gridSize),
				(int)((endPoint.Y - bounds.Y) / gridSize));
			if (ptStart.X == ptEnd.X && ptStart.Y == ptEnd.Y)
				return;

			// init the route grid
			int gridCols = (int)(bounds.Width / gridSize);
			int gridRows = (int)(bounds.Height / gridSize);

			RoutingGrid routingGrid = flowChart.RoutingGrid;
			routingGrid.allocate(gridCols, gridRows, bounds, this);
			byte[,] grid = routingGrid.getCostGrid();
			PathNode[,] gridClosed = routingGrid.getClosedGrid();
			PathNode[,] gridOpen = routingGrid.getOpenGrid();

			bool hurry = (gridCols * gridRows > 90000) &&
				flowChart.RoutingOptions.DontOptimizeLongRoutes;
			RouteHeuristics calcRouteHeuristics = hurry ?
				RoutingOptions.DistSquare : flowChart.RoutingOptions.RouteHeuristics;

			routeFixEndRegions(grid, ref ptStart, oNearest, ref ptEnd, dNearest, gridCols, gridRows);
			grid[ptStart.X, ptStart.Y] = 0;
			grid[ptEnd.X, ptEnd.Y] = 0;

			//---------- A* algorithm initialization -----------
			SortedList open = new SortedList();
			ArrayList closed = new ArrayList();
			Stack stack = new Stack();

			PathNode temp = new PathNode(ptStart.X, ptStart.Y);
			temp.G = 0;
			temp.H = calcRouteHeuristics(ptStart, ptEnd);
			temp.F = temp.G + temp.H;
			open.Add(temp, temp);
			gridOpen[temp.X, temp.Y] = temp;

			// setup A* cost function
			int adjcCost = flowChart.RoutingOptions.LengthCost;
			int turnCost = flowChart.RoutingOptions.TurnCost;

			PathNode best = null;
			bool found = false;
			int iterations = 0;
			for ( ; ; )
			{
				iterations++;

				// Get the best node from the open list
				if (open.Count == 0) break;
				PathNode pstmp = open.GetByIndex(0) as PathNode;

				open.RemoveAt(0);
				gridOpen[pstmp.X, pstmp.Y] = null;

				closed.Add(pstmp);
				gridClosed[pstmp.X, pstmp.Y] = pstmp;

				if ((best = pstmp) == null) break;

				// If best == destination -> path found
				if (best.X == ptEnd.X && best.Y == ptEnd.Y)
				{
					found = true;
					break;
				}

				// Generate best's successors
				int x = best.X;
				int y = best.Y;

				int[,] off = new int[4, 2] { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
				for (i = 0; i < 4; i++)
				{
					byte localCost = grid[x + off[i, 0], y + off[i, 1]];
					if (localCost == 255)
						continue;

					int g = best.G + adjcCost + localCost;
					bool straight = best.Parent == null ||
						(best.Parent.Y == best.Y && off[i, 1] == 0) ||
						(best.Parent.X == best.X && off[i, 0] == 0);
					if (best.Parent == null && oNearest >= 0 && (
						oNearest < 2 && off[i, 1] == 0 || oNearest >= 2 && off[i, 1] == 1))
						straight = false;
					if (!straight) g += turnCost;

					PathNode check = null;

					// if the successor is an open node, add it to the path
					check = gridOpen[x + off[i, 0], y + off[i, 1]];
					if (check != null)
					{
						best.Children[best.ChildCount++] = check;

						// and update its cost if now it is reached via a better path
						if (g < check.G)
						{
							open.Remove(check);		// keep sorted
							check.Parent = best;
							check.G = g;
							check.F = g + check.H;
							open.Add(check, check);	// keep sorted
						}
					}
					else
					{
						// if the successor is a closed node, add it to the path
						check = gridClosed[x + off[i, 0], y + off[i, 1]];
						if (check != null)
						{
							best.Children[best.ChildCount++] = check;

							// and update its cost if now it is reached via a better path
							if (g < check.G)
							{
								check.Parent = best;
								check.G = g;
								check.F = g + check.H;

								// and update its child items
								int gg = check.G;
								int cc = check.ChildCount;
								PathNode kid = null;
								for (int j = 0; j < cc; j++)
								{
									kid = check.Children[j];

									int gi = adjcCost;
									straight = check.Parent == null ||
										(check.Parent.Y == check.Y && check.Y == kid.Y) ||
										(check.Parent.X == check.X && check.X == kid.X);
									if (!straight) gi += turnCost;

									if (g + gi < kid.G)
									{
										bool wasOpen = gridOpen[kid.X, kid.Y] != null;
										if (wasOpen) open.Remove(kid);	// keep sorted

										kid.G = g + gi;
										kid.F = kid.G + kid.H;
										kid.Parent = check;
										stack.Push(kid);

										if (wasOpen) open.Add(kid, kid);
									}
								}
								PathNode parent;
								while (stack.Count > 0)
								{
									parent = stack.Pop() as PathNode;
									cc = parent.ChildCount;
									for (int j = 0; j < cc; j++)
									{
										kid = parent.Children[j];

										int gi = adjcCost;
										straight = parent.Parent == null ||
											(parent.Parent.Y == parent.Y && parent.Y == kid.Y) ||
											(parent.Parent.X == parent.X && parent.X == kid.X);
										if (!straight) gi += turnCost;

										if (parent.G + gi < kid.G)
										{
											bool wasOpen = gridOpen[kid.X, kid.Y] != null;
											if (wasOpen) open.Remove(kid);	// keep sorted

											kid.G = parent.G + gi;
											kid.F = kid.G + kid.H;
											kid.Parent = parent;
											stack.Push(kid);

											if (wasOpen) open.Add(kid, kid);
										}
									}
								}
							}
						}
						else
						{
							// haven't considered this grid square by now
							// create and initialize a path node for it 
							Point current = new Point(x + off[i, 0], y + off[i, 1]);
							PathNode newNode = new PathNode(current.X, current.Y);
							newNode.Parent = best;
							newNode.G = g;
							newNode.H = calcRouteHeuristics(current, ptEnd);
							newNode.F = newNode.G + newNode.H;

							// add it to the list of open nodes to be evaluated later
							open.Add(newNode, newNode);
							gridOpen[newNode.X, newNode.Y] = newNode;

							// add to the path
							best.Children[best.ChildCount++] = newNode;
						}
					}
				}
			}

			if (found)
			{
				PtCollection current = new PtCollection(0);

				current.Add(new Point((int)((points[points.Count - 1].X - bounds.X) / gridSize),
					(int)((points[points.Count - 1].Y - bounds.Y) / gridSize)));
				while (best != null)
				{
					current.Add(new Point(best.X, best.Y));
					best = best.Parent;
				}
				current.Add(new Point((int)((points[0].X - bounds.X) / gridSize),
					(int)((points[0].Y - bounds.Y) / gridSize)));

				// Remove all unneeded points
				Point pt1, pt2, pt3;
				for (i = 1; i < current.Count - 1;)
				{
					pt1 = current[i - 1];
					pt2 = current[i];
					pt3 = current[i + 1];

					if (pt1.X == pt2.X && pt2.X == pt3.X)
						current.RemoveAt(i);
					else if(pt1.Y == pt2.Y && pt2.Y == pt3.Y)
						current.RemoveAt(i);
					else
						i++;
				}

				// Save the first and last points of the arrow
				PointF ptFirst = points[0];
				PointF ptLast = points[points.Count - 1];

				// no perp. arrows on a single line
				if (style == ArrowStyle.Cascading && current.Count == 2 &&
					ptFirst.X != ptLast.X && ptFirst.Y != ptLast.Y)
				{
					Point orgPt = current[0];
					Point trgPt = current[current.Count-1];
					if (orgPt.X == trgPt.X || orgPt.Y == trgPt.Y)
					{
						Point insPt = new Point(
							(orgPt.X + trgPt.X) / 2, (orgPt.Y + trgPt.Y) / 2);
						current.Insert(1, insPt);
						current.Insert(1, insPt);
					}
				}

				// Re-segment the arrow
				points = new PointCollection(current.Count);
				points[0] = ptFirst;
				points[points.Count - 1] = ptLast;

				// Assign the points from the path
				i = current.Count - 1;
				i--; // Skip the first point
				while (i > 0)
				{
					Point pt = current[i];
					PointF ptDoc = new PointF(0, 0);
					ptDoc.X = bounds.X + pt.X * gridSize + gridSize / 2;
					ptDoc.Y = bounds.Y + pt.Y * gridSize + gridSize / 2;

					if (i == 1)
					{
						// Align to the last point
						if (pt.Y == current[0].Y)
							ptDoc.Y = ptLast.Y;
						else
							ptDoc.X = ptLast.X;
					}
					if (i == current.Count - 2)
					{
						// Align to the first point
						if (pt.Y == current[current.Count - 1].Y)
							ptDoc.Y = ptFirst.Y;
						else
							ptDoc.X = ptFirst.X;

						if (style == ArrowStyle.Cascading)
							cascadeStartHorizontal = (ptDoc.X != ptFirst.X);
					}

					points[current.Count - i - 1] = ptDoc;
					i--;
				}

				PointF ptf, ptf1, ptf2, ptf3;

				// If the line is perpendicular make it at least 2 segments
				if(style == ArrowStyle.Cascading && points.Count == 2)
				{
					ptf1 = points[0];
					ptf2 = points[points.Count - 1];
					ptf = ptf1;
					if (cascadeStartHorizontal)
						ptf.X = ptf2.X;
					else
						ptf.Y = ptf2.Y;
					points.Insert(1, ptf);
				}

				// If the line is straight there might be more unneeded points
				if (style == ArrowStyle.Polyline)
				{
					i = 0;
					while(i < points.Count - 2)
					{
						ptf1 = points[i];
						ptf2 = points[i + 2];

						ChartObject obj = flowChart.objectIntersectedBy(ptf1, ptf2,
							orgnLink.getNode(), destLink.getNode());
						if(obj == null)
							points.RemoveAt(i + 1);
						else
							i++;
					}
				}

				// If the line is bezier, smooth it a bit
				if (style == ArrowStyle.Bezier)
				{
					PointCollection newPoints = new PointCollection(0);
					newPoints.Add(points[0]);
					i = 0;
					while(i < points.Count - 2)
					{
						ptf1 = points[i];
						ptf2 = points[i + 1];

						newPoints.Add(ptf2);
						newPoints.Add(ptf2);
						if(i != points.Count - 3)
						{
							ptf3 = points[i + 2];
							ptf = new PointF((ptf2.X + ptf3.X) / 2, (ptf2.Y + ptf3.Y) / 2);
							newPoints.Add(ptf);
						}
						else
						{
							newPoints.Add(points[i + 2]);
						}
						i += 1;
					}

					if (newPoints.Count == 1)
					{
						newPoints = new PointCollection(4);

						ptf1 = points[0];
						ptf2 = points[points.Count - 1];
						ptf = new PointF((ptf1.X + ptf2.X) / 2, (ptf1.Y + ptf2.Y) / 2);
						newPoints[0] = ptf1;
						newPoints[1] = ptf;
						newPoints[2] = ptf;
						newPoints[3] = ptf2;
					}

					points.Clear();
					points = newPoints;
				}

				// Update SegmentCount property value
				if (style == ArrowStyle.Bezier)
					segmentCount = (short)((points.Count - 1) / 3);
				else
					segmentCount = (short)(points.Count - 1);
			}
			else
			{
				// No path found -> reset the arrow, leaving as little points as possible
				int ptsToLeave = 2;
				if (style == ArrowStyle.Cascading)
					ptsToLeave = 4;
				else if (style == ArrowStyle.Bezier)
					ptsToLeave = 4;

				if (style == ArrowStyle.Cascading)
				{
					cascadeOrientation = Orientation.Auto;
					segmentCount = 3;
				}
				else
					segmentCount = 1;

				while (points.Count > ptsToLeave)
					points.RemoveAt(1);

				if (style == ArrowStyle.Cascading && points.Count == 3)
					segmentCount = 2;

				updatePoints(points[points.Count - 1]);
			}

			updateArrowHeads();

			if (subordinateGroup != null)
			{
				subordinateGroup.onSegmentsChanged();
				subordinateGroup.updateObjects(new InteractionState(this, -1, Action.Modify));
			}

			resetCrossings();
			updateText();

			flowChart.fireArrowRoutedEvent(this);
		}