public ArrowUtils( ArrowStyle pstyle, PointCollection pPoints ) { points = pPoints.Clone(); style = pstyle; // // TODO: Add constructor logic here // }
public static PointF GetBezierPt(PointCollection points, int segment, float t) { float x0 = points[segment * 3 + 0].X; float y0 = points[segment * 3 + 0].Y; float x1 = points[segment * 3 + 1].X; float y1 = points[segment * 3 + 1].Y; float x2 = points[segment * 3 + 2].X; float y2 = points[segment * 3 + 2].Y; float x3 = points[segment * 3 + 3].X; float y3 = points[segment * 3 + 3].Y; float tt = t; float q0 = (1-tt)*(1-tt)*(1-tt); float q1 = 3*tt*(1-tt)*(1-tt); float q2 = 3*tt*tt*(1-tt); float q3 = tt*tt*tt; float xt = q0*x0 + q1*x1 + q2*x2 + q3*x3; float yt = q0*y0 + q1*y1 + q2*y2 + q3*y3; return new PointF(xt, yt); }
internal void onArrowSplit(bool added, int idx, int cnt) { if (added) { foreach (Attachment attc in attachments) { switch (attc.type) { case AttachTo.ArrowPoint: { int point = attc.attData; if (point >= idx) { attc.attData += cnt; } } break; case AttachTo.ArrowSegment: { int segment = attc.attData; if (segment >= idx) { attc.attData += cnt; } } break; } } // for } else { foreach (Attachment attc in attachments) { switch (attc.type) { case AttachTo.ArrowPoint: { int point = attc.attData; if (point >= idx) { if (point < idx + cnt) { attc.attData = idx; } else { attc.attData -= cnt; } } } break; case AttachTo.ArrowSegment: { int segment = attc.attData; if (segment >= idx) { if (segment < idx + cnt) { attc.attData = idx - 1; } else { attc.attData -= cnt; } if (attc.attData < 0) { attc.attData = 0; } } } break; } } // for } prevPoints = ((Arrow)mainObj).Points.Clone(); }
/// <summary> /// Gets rectangle of the arrow text /// </summary> /// <param name="g">GraphicsPath</param> /// <param name="style">Style of the arrow</param> /// <param name="textStyle">Style of the arrow text</param> /// <param name="points">Arrow's point collection</param> /// <param name="textColor">Text color</param> /// <param name="segmentCount">Arrow segments count</param> /// <param name="text">Arrow's text itself</param> /// <param name="textFont">Arrow's text font</param> /// <param name="rect">Bounding rect</param> /// <param name="a">Text's rotation angle to be returned</param> /// <returns>Arrow text's rectangle</returns> private RectangleF getTextRect(Graphics g , ArrowStyle style, ArrowTextStyle textStyle, PointCollection points , Color textColor ,int segmentCount , string text , Font textFont , RectangleF rect , ref float a ) { // DrawString crashes with a too small PageScale if (g.PageScale < 0.01) return RectangleF.Empty; System.Drawing.Brush brText = new System.Drawing.SolidBrush(textColor); StringFormat sf = StringFormat.GenericDefault; sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Far; float x =0 , y = 0; a = 0; ArrowUtils au = new ArrowUtils(style,points ); System.Drawing.SizeF textSize = g.MeasureString(text, textFont); if (textStyle == ArrowTextStyle.Rotate) { // Find the segment near which the text will be drawn int pt = points.Count / 2 - 1; int jump = 1; int ptBest = pt; float r = 0, rBest = 0; if (style != ArrowStyle.Bezier) { if (style == ArrowStyle.Cascading || (style == ArrowStyle.Polyline && segmentCount % 2 == 1)) { do { MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( points[pt], points[pt + 1], ref a, ref r); if (r > rBest) { rBest = r; ptBest = pt; } pt = pt + jump; jump = -Math.Sign(jump) * (Math.Abs(jump) + 1); if(pt < 0 || pt >= points.Count - 1) break; } while(textSize.Width > r); pt = ptBest; x = (points[pt].X + points[pt + 1].X) / 2; y = (points[pt].Y + points[pt + 1].Y) / 2; } else { pt = pt + 1; x = points[pt].X; y = points[pt].Y; } } else { if(segmentCount % 2 == 1) { pt = (pt + 1) / 4; PointF ptMid = ArrowUtils.GetBezierPt(points, pt, 0.5f); x = ptMid.X; y = ptMid.Y; } else { pt = (pt + 1) / 4; PointF ptMid = points[(pt + 1) * 3]; x = ptMid.X; y = ptMid.Y; } } // Find out the angle if (style != ArrowStyle.Bezier) { if( style == ArrowStyle.Cascading || (style == ArrowStyle.Polyline && segmentCount % 2 == 1)) { PointF pt1 = points[pt]; PointF pt2 = points[pt + 1]; MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( pt1, pt2, ref a, ref r); } else { float r1 = 0, a1 = 0; MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( points[pt - 1], points[pt], ref a, ref r); MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( points[pt], points[pt + 1], ref a1, ref r1); a = (a + a1) / 2; } } else { if (segmentCount % 2 == 1) { PointF pt1 = ArrowUtils.GetBezierPt(points, pt, 0.45f); PointF pt2 = ArrowUtils.GetBezierPt(points, pt, 0.55f); MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( pt1, pt2, ref a, ref r); } else { PointF pt1 = ArrowUtils.GetBezierPt(points, pt, 0.95f); PointF pt2 = ArrowUtils.GetBezierPt(points, pt + 1, 0.05f); MindFusion.Geometry.Geometry2D.Convert.DekartToPolar( pt1, pt2, ref a, ref r); } } a = 180 - a; if (a > 90 && a < 270) a -= 180; } else if (textStyle == ArrowTextStyle.Center) { if(points.Count % 2 == 1) { x = points[points.Count / 2].X; y = points[points.Count / 2].Y; } else { x = points[points.Count / 2 - 1].X + points[points.Count / 2].X; y = points[points.Count / 2 - 1].Y + points[points.Count / 2].Y; x /= 2; y /= 2; } //g.DrawString(text, textFont, brText, new PointF(x, y), sf); } else if (textStyle == ArrowTextStyle.OverLongestSegment) { // find the center point of longest segment int longest = au.getLongestSegment(); PointF center = au.getSegmentCenter(longest); // see how much space the text requires StringFormat cf = m_FlowChart.TextFormat; SizeF size = g.MeasureString(text, textFont); size.Width += ArrowUtils.getMillimeter(m_FlowChart.MeasureUnit); size.Height += ArrowUtils.getMillimeter(m_FlowChart.MeasureUnit); } brText.Dispose(); RectangleF rc = new RectangleF(x - textSize.Width/2 ,y - textSize.Height/2,textSize.Width, textSize.Height); return rc; }
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; }
internal void drawArrow(Graphics g, Arrow arrow, bool shadow, PointCollection points) { if (DrawArrow != null) DrawArrow(this, new ArrowDrawArgs(g, arrow, shadow, points)); }
public static PointCollection approxEllipse(RectangleF rect, int quality) { PointCollection approximation = new PointCollection(0); float epsilon = 1.0f / quality; // get the points defining the curve float a = rect.Width / 2; float b = rect.Height / 2; float cx = rect.X + a; float cy = rect.Y + b; for (float t = 0; t <= 3.1415f * 2; t += epsilon) { float x = (float)(cx + a * Math.Cos(t)); float y = (float)(cy + b * Math.Sin(t)); // draw straight line between last two calculated points approximation.Add(new PointF(x, y)); } return approximation; }
internal static float distToPolyline(PointF pt, PointCollection line_pts, int nPoints, ref int segmNum) { float nDist, nMinDist = 1000000; segmNum = 0; for (int l = 0; l < nPoints - 1; ++l) { double x1 = line_pts[l].X; double y1 = line_pts[l].Y; double x2 = line_pts[l+1].X; double y2 = line_pts[l+1].Y; double A = y1 - y2; double B = x2 - x1; double C = x1*y2 - y1*x2; if (Math.Abs(A) < 0.00001 && Math.Abs(B) < 0.00001) { nDist = (float)Math.Sqrt((x1-pt.X)*(x1-pt.X) + (y1-pt.Y)*(y1-pt.Y)); } else if (Math.Abs(A) < 0.00001) { if ((pt.X > x1 && pt.X > x2) || (pt.X < x1 && pt.X < x2)) { nDist = (float)Math.Min( Math.Sqrt((x1-pt.X)*(x1-pt.X) + (y1-pt.Y)*(y1-pt.Y)), Math.Sqrt((x2-pt.X)*(x2-pt.X) + (y2-pt.Y)*(y2-pt.Y))); } else { nDist = (float)Math.Abs(y1 - pt.Y); } } else { double xo, yo; yo = (A*A*pt.Y - A*B*pt.X - B*C) / (A*A + B*B); xo = (-C - B*yo) / A; if ( (xo - x1)*(xo - x2) <= 0 && (yo - y1)*(yo - y2) <= 0) { nDist = (float)Math.Sqrt((pt.X - xo)*(pt.X - xo) + (pt.Y - yo)*(pt.Y - yo)); } else { nDist = (float)Math.Min( Math.Sqrt((x1-pt.X)*(x1-pt.X) + (y1-pt.Y)*(y1-pt.Y)), Math.Sqrt((x2-pt.X)*(x2-pt.X) + (y2-pt.Y)*(y2-pt.Y))); } } if (nMinDist > nDist) { nMinDist = nDist; segmNum = l; } } return nMinDist; }
internal override void restoreState(ItemState state) { base.restoreState(state); ArrowState astate = (ArrowState)state; style = astate.style; segmentCount = astate.segmentCount; points = astate.points.Clone(); reflexive = astate.reflexive; cascadeStartHorizontal = astate.cascadeStartHorizontal; if (orgnLink != astate.orgnLink) { orgnLink.removeArrowFromObj(); orgnLink = astate.orgnLink; orgnLink.addArrowToObj(); } orgnAnchor = astate.orgnAnchor; if (destLink != astate.destLink) { destLink.removeArrowFromObj(); destLink = astate.destLink; destLink.addArrowToObj(); } destAnchor = astate.destAnchor; updateFromPoints(false); if (subordinateGroup != null) subordinateGroup.onRestoreState(); orgnLink.RelativePosition = astate.orgnPoint; destLink.RelativePosition = astate.destPoint; resetCrossings(); updateText(); }
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); }
private void restoreSegments(ArrowSegmentsState state) { points = state.controlPoints.Clone(); segmentCount = state.segmentCount; cascadeStartHorizontal = state.cascadeStartHorizontal; }
private void setSegments(short segments) { int ctrlPoints = 2; switch (style) { case ArrowStyle.Bezier: ctrlPoints = segments*3 + 1; break; case ArrowStyle.Polyline: ctrlPoints = segments + 1; break; case ArrowStyle.Cascading: ctrlPoints = segments + 1; break; } points = new PointCollection(ctrlPoints); segmentCount = segments; if (subordinateGroup != null) subordinateGroup.onSegmentsChanged(); }
// ************ drawing ************ internal void drawArrowSegments(Graphics g, System.Drawing.Pen p, System.Drawing.Brush b, System.Drawing.Pen pHeads, bool shadow, bool custom) { PointCollection pts = points; float xoff = shadow ? ShadowOffsetX : 0; float yoff = shadow ? ShadowOffsetY : 0; // offset if drawing shadow if (shadow) { pts = new PointCollection(points.Count); for (int i = 0; i < pts.Count; ++i) { pts[i] = new PointF( points[i].X + xoff, points[i].Y + yoff); } } if (custom && (customDraw == CustomDraw.Full || (customDraw == CustomDraw.ShadowOnly && shadow))) { // call the custom draw function flowChart.drawArrow(g, this, shadow, pts); } else { // draw the arrow's line if (style == ArrowStyle.Bezier) { g.DrawBeziers(p, pts.getArray()); } else { float mm = Constants.getMillimeter(flowChart.MeasureUnit); float pathThresh = mm / 3; if (!drawCrossings || flowChart.ArrowCrossings == MindFusion.FlowChartX.ArrowCrossings.Straight) { if (flowChart.RoundedArrows) { DrawRoundedPolyline(g, p, pts.getArray(), flowChart.RoundedArrowsRadius, true, pathThresh, null); } else { g.DrawLines(p, pts.getArray()); } } else { float crad = flowChart.CrossingRadius; ArrowCrossings crossings = getCrossings(); object startPoint = null; GraphicsPath gpath = pen.Width > pathThresh ? new GraphicsPath() : null; for (int i = 0; i < segmentCount; ++i) { PointCollection pc = crossings.segmentCrossings[i] as PointCollection; if (pc.Count > 0) { for (int j = 0; j < pc.Count - 1; ++j) { PointF pt1 = pc[j]; PointF pt2 = pc[j + 1]; pt1.X += xoff; pt1.Y += yoff; pt2.X += xoff; pt2.Y += yoff; if (startPoint != null) pt1 = (PointF)startPoint; startPoint = null; if (j % 2 == 0) { // The subsegment between two crossings or // between a crossing and a segment end-point if (flowChart.RoundedArrows) { // Check if this is the last subsegment // in this segment. If that is the case, // and this is not the last segment of the // arrow, draw rounded arrow if (j == pc.Count - 2 && i != segmentCount - 1) { // The third point in the poly is // the second point of the next segment // if it does not have crossings, or // the second point of the crossings array int ni = i + 2; PointF next = pts[ni]; while (Math.Abs(next.X - pt2.X) + Math.Abs(next.Y - pt2.Y) < 0.00001f) { ni++; if (ni == pts.Count) break; next = pts[ni]; } if (ni == pts.Count) { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } else { PointCollection nextPc = crossings.segmentCrossings[ni - 1] as PointCollection; if (nextPc.Count > 2) { next = nextPc[1]; next.X += xoff; next.Y += yoff; } PointF[] triPoints = new PointF[] { pt1, pt2, next }; startPoint = DrawRoundedPolyline(g, p, triPoints, flowChart.RoundedArrowsRadius, false, pathThresh, gpath); } } else { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } } else { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } } else { if (flowChart.ArrowCrossings == MindFusion.FlowChartX.ArrowCrossings.Arcs) { float rad = Utilities.Distance(pt1, pt2) / 2; float aa = 0; float rr = 0; Geometry.Geometry2D.Convert.DekartToPolar( pt1, pt2, ref aa, ref rr); PointF[] centers = new PointF[] { PointF.Empty, PointF.Empty }; Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, crad, ref centers[0]); Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * rad - crad, ref centers[1]); PointF[] startPts = new PointF[] { pt1, PointF.Empty }; PointF[] endPts = new PointF[] { PointF.Empty, pt2 }; Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * crad, ref endPts[0]); Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * rad - 2 * crad, ref startPts[1]); float angle = aa; if (angle < 90) angle += 180; RectangleF rc = RectangleF.FromLTRB( centers[0].X - crad, centers[0].Y - crad, centers[0].X + crad, centers[0].Y + crad); float ded = 0 * 90; if (aa < 90) ded = 90 - ded; float start = 180 - angle - ded; float sweep = -90; if (aa < 90) { start += sweep; sweep = -sweep; } if (gpath != null) gpath.AddArc(rc, start, sweep); else g.DrawArc(p, rc, start, sweep); PointF p1 = PointF.Empty; PointF p2 = PointF.Empty; Geometry.Geometry2D.Convert.PolarToDekart( centers[0], angle - 90, crad, ref p1); Geometry.Geometry2D.Convert.PolarToDekart( centers[1], angle - 90, crad, ref p2); if (gpath != null) gpath.AddLine(p1, p2); else g.DrawLine(p, p1, p2); rc = RectangleF.FromLTRB( centers[1].X - crad, centers[1].Y - crad, centers[1].X + crad, centers[1].Y + crad); ded = 1 * 90; if (aa < 90) ded = 90 - ded; start = 180 - angle - ded; sweep = -90; if (aa < 90) { start += sweep; sweep = -sweep; } if (gpath != null) gpath.AddArc(rc, start, sweep); else g.DrawArc(p, rc, start, sweep); } else { // Start new figure in the graph, // thus preventing the graph // to automatically connect broken // lines and losing break-offs if (gpath != null) gpath.StartFigure(); } } } } else { if (gpath != null) gpath.AddLine(pts[i], pts[i+1]); else g.DrawLine(p, pts[i], pts[i+1]); } } if (gpath != null) { gpath.Flatten(new Matrix(), 0.05f); g.DrawPath(p, gpath); gpath.Dispose(); } } } // draw arrowheads, intermediate are skipped for asBezier arrows ahBase.draw(g, pHeads, b, xoff, yoff); if (style != ArrowStyle.Bezier && arrowInterm != ArrowHead.None) { for (int i = 0; i < points.Count - 1; i++) { PointF pt1 = pts[i]; PointF pt2 = new PointF((pt1.X + pts[i+1].X)/2, (pt1.Y + pts[i+1].Y)/2); headTemplates[(int)arrowInterm].recalcArrowHead(ahInterm, pt1, pt2); ahInterm.draw(g, pHeads, b, 0, 0); } } ahHead.draw(g, pHeads, b, xoff, yoff); // additional custom draw type if (custom && customDraw == CustomDraw.Additional) flowChart.drawArrow(g, this, false, pts); } }
internal override void completeModify(PointF end, InteractionState ist) { // make sure the modification does not propagate through endless recursion if (cycleProtect) return; cycleProtect = true; if (!(snapToNodeBorder && objNewDest != null)) end = flowChart.AlignPointToGrid(end); // snap to anchor point Node objNode = null; if (modifyHandle == 0 || modifyHandle == points.Count-1) { objNode = flowChart.GetNodeAt(end); // align to node border if that's enabled if (snapToNodeBorder && objNode != null) end = objNode.getNearestOutlinePt(end); // align to anchor point int idx = -1; if (objNode != null && flowChart.SnapToAnchor == SnapToAnchor.OnCreateOrModify) end = objNode.getAnchor(end, this, modifyHandle != 0, ref idx); if (modifyHandle == 0) orgnAnchor = idx; else destAnchor = idx; } // Route only if either the first or the last point is modified bool shouldRoute = (modifyHandle == 0 || modifyHandle == points.Count - 1); // update ctrl. point position base.completeModify(end, ist); points[modifyHandle] = end; updateRelatedPoints(end, ist); // update the links if the origin object has changed if (modifyHandle == 0) { PointF pt = points[0]; if (objNode == null) objNode = flowChart.GetNodeAt(pt); if (objNode == null) objNode = flowChart.Dummy; if (objNode != null && orgnLink.linkChanges(objNode, pt)) { orgnLink.removeArrowFromObj(); orgnLink = null; orgnLink = objNode.createLink(this, pt, false); orgnLink.addArrowToObj(); reflexive = orgnLink.sameNode(destLink); } orgnLink.saveEndRelative(); } else // update the links if the target object has changed if (modifyHandle == points.Count-1) { PointF pt = points[points.Count-1]; if (objNode == null) objNode = flowChart.GetNodeAt(pt); if (objNode == null) objNode = flowChart.Dummy; if (objNode != null && destLink.linkChanges(objNode, pt)) { destLink.removeArrowFromObj(); destLink = null; destLink = objNode.createLink(this, pt, true); destLink.addArrowToObj(); reflexive = destLink.sameNode(orgnLink); } destLink.saveEndRelative(); } else // combine arrow segments ? { PointF ptPrev = points[modifyHandle - 1]; PointF ptCurr = points[modifyHandle]; PointF ptNext = points[modifyHandle + 1]; // for polylines: if the angle between them is ~180 if (style == ArrowStyle.Polyline && flowChart.AllowSplitArrows) { PointCollection pts = new PointCollection(2); pts.SetAt(0, ptPrev); pts.SetAt(1, ptNext); float dx = ptNext.X - ptPrev.X; float dy = ptNext.Y - ptPrev.Y; float len = (float)Math.Sqrt(dx*dx + dy*dy); float d = Utilities.distToPolyline(ptCurr, pts, 2); if (d*35 < len) { points.RemoveAt(modifyHandle); segmentCount--; if (subordinateGroup != null) subordinateGroup.onArrowSplit(false, modifyHandle, 1); } } // combine segments of cascading arrows if two adjacent control points overlap if (style == ArrowStyle.Cascading && segmentCount > 3 && flowChart.AllowSplitArrows) { int toDel = -1; if (modifyHandle > 1 && flowChart.mergePoints(ptPrev, ptCurr)) toDel = modifyHandle - 1; if (modifyHandle < points.Count - 2 && flowChart.mergePoints(ptNext, ptCurr)) toDel = modifyHandle; if (toDel != -1) { points.RemoveAt(toDel); points.RemoveAt(toDel); segmentCount -= 2; alignCascadingSegments(toDel); updateEndPtsPrp(); if (subordinateGroup != null) subordinateGroup.onArrowSplit(false, toDel, 2); } } } if (reflexive && (style == ArrowStyle.Polyline && segmentCount == 1 || style == ArrowStyle.Cascading && segmentCount == 2)) { setReflexive(); if (subordinateGroup != null) subordinateGroup.endModification(); cycleProtect = false; objNewDest = null; return; } if (shouldRoute && autoRoute && flowChart.rerouteArrow(this)) doRoute(); updateArrowHeads(); if (subordinateGroup != null) subordinateGroup.endModification(); resetCrossings(); updateText(); flowChart.fireObjModified(this, end, modifyHandle); cycleProtect = false; objNewDest = null; }
public ArrowDrawArgs(Graphics g, Arrow arrow, bool shadow, PointCollection points) : base(arrow) { this.graphics = g; this.shadow = shadow; this.points = points; }
private string createPolyArea(PointCollection points) { string strCoords = "\""; Point pt = new Point(0,0); for (int j = 0; j < points.Count; ++j) { PointF docPt = points[j]; docPt.X -= docOffsetX; docPt.Y -= docOffsetY; pt = DocToClient(docPt); strCoords += pt.X.ToString() + "," + pt.Y.ToString(); if (j < points.Count - 1) strCoords += ","; } strCoords = strCoords + "\""; return "\"POLY\" COORDS=" + strCoords; }
internal static float distToPolyline(PointF pt, PointCollection line_pts, int nPoints) { int segmNum = 0; return distToPolyline(pt, line_pts, nPoints, ref segmNum); }
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; }
public static PointCollection approxBezier(PointF[] points, int startIdx, int quality) { PointCollection approximation = new PointCollection(0); double epsilon = 1.0 / quality; // get the points defining the curve float x0 = points[0 + startIdx].X; float y0 = points[0 + startIdx].Y; float x1 = points[1 + startIdx].X; float y1 = points[1 + startIdx].Y; float x2 = points[2 + startIdx].X; float y2 = points[2 + startIdx].Y; float x3 = points[3 + startIdx].X; float y3 = points[3 + startIdx].Y; // x(t) = (1-t)^3 x0 + 3t(1-t)^2 x1 + 3t^2 (1-t) x2 + t^3 x3 // y(t) = (1-t)^3 y0 + 3t(1-t)^2 y1 + 3t^2 (1-t) y2 + t^3 y3 for (double t = 0; t <= 1.0; t += epsilon) { double q0 = (1-t)*(1-t)*(1-t); double q1 = 3*t*(1-t)*(1-t); double q2 = 3*t*t*(1-t); double q3 = t*t*t; double xt = q0*x0 + q1*x1 + q2*x2 + q3*x3; double yt = q0*y0 + q1*y1 + q2*y2 + q3*y3; // draw straight line between last two calculated points approximation.Add(new PointF((float)xt, (float)yt)); } return approximation; }
internal override PointCollection getOutlinePoly() { PointCollection poly = new PointCollection(0); GraphicsPath arrowPath = new GraphicsPath(FillMode.Alternate); // draw the arrow's line if (style == ArrowStyle.Bezier) { arrowPath.AddBeziers(points.getArray()); } else { arrowPath.AddLines(points.getArray()); } System.Drawing.Pen widenPen = new System.Drawing.Pen(Color.Black, 2 * Constants.getMillimeter(flowChart.MeasureUnit)); arrowPath.Widen(widenPen); arrowPath.Flatten(); poly.AddRange(arrowPath.PathPoints); widenPen.Dispose(); arrowPath.Dispose(); return poly; }
internal static float distToBezier(PointF pt, PointCollection bez_pts, int nPoints) { int nSplineNum = nPoints / 3; float nRes = 1000000; for (int nSpline = 0; nSpline < nSplineNum; ++nSpline) { double x0 = bez_pts[nSpline*3+0].X; double y0 = bez_pts[nSpline*3+0].Y; double x1 = bez_pts[nSpline*3+1].X; double y1 = bez_pts[nSpline*3+1].Y; double x2 = bez_pts[nSpline*3+2].X; double y2 = bez_pts[nSpline*3+2].Y; double x3 = bez_pts[nSpline*3+3].X; double y3 = bez_pts[nSpline*3+3].Y; double a3 = (x3 - x0 + 3*(x1-x2)) / 8; double b3 = (y3 - y0 + 3*(y1-y2)) / 8; double a2 = (x3 + x0 - x1 - x2) * 3 / 8; double b2 = (y3 + y0 - y1 - y2) * 3 / 8; double a1 = (x3 - x0) / 2 - a3; double b1 = (y3 - y0) / 2 - b3; double a0 = (x3 + x0) / 2 - a2; double b0 = (y3 + y0) / 2 - b2; double x4 = pt.X; double y4 = pt.Y; double s1, s=0, u1, u2, z=0, z1, z2; s1 = 0; u2 = u1 = -1.0; z1 = z2 = 0; double stepsize = 2.0 / 9; double u = 0; for (u = -1.0; u < 1.0; u += stepsize) { calcBezierCoef(ref a0, ref a1, ref a2, ref a3, ref b0, ref b1, ref b2, ref b3, ref u, ref s, ref z, ref x4, ref y4); if (Math.Abs(s) < 0.00001) { u1 = u; z1 = z; s1 = s; break; } if (Math.Abs(u + 1.0) < 0.00001) { u1 = u; z1 = z; s1 = s; } if (s < s1) { u1 = u; z1 = z; s1 = s; } } if (Math.Abs(s1) > 0.00001) { u = u1 + stepsize; if (u > 1.0) u = 1.0 - stepsize; for(int cnt = 0 ; cnt < 20; cnt++) { calcBezierCoef(ref a0, ref a1, ref a2, ref a3, ref b0, ref b1, ref b2, ref b3, ref u, ref s, ref z, ref x4, ref y4); if (Math.Abs(s) < 0.00001) break; if (Math.Abs(z) < 0.00001) break; u2 = u; z2 = z; double temp = z2 - z1; if (Math.Abs(temp) > 0.00001) { u = (z2*u1 - z1*u2) / temp; } else { u = (u1 + u2) / 2; } if (u > 1.0) { u = 1.0; } else if (u < -1.0) { u = -1.0; } if (Math.Abs(u - u2) < 0.0001) break; u1 = u2; z1 = z2; } } if (nRes > Math.Sqrt(s)) nRes = (float)Math.Sqrt(s); if (nRes == 0) return 0; } return nRes; }
internal static bool CompareControlPoints(PointCollection pointCollection, List<SysCAD.Protocol.Point> list) { if (pointCollection.Count == list.Count) return false; for (int i = 0; i < list.Count; i++) { if (pointCollection[i].X != list[i].X) return false; if (pointCollection[i].Y != list[i].Y) return false; } return true; }
internal override PointCollection getOutlinePoly() { RectangleF br = BoundingRect; PointCollection points = new PointCollection(0); points.Add(new PointF(br.Left, br.Top)); points.Add(new PointF(br.Right, br.Top)); points.Add(new PointF(br.Right, br.Bottom)); points.Add(new PointF(br.Left, br.Bottom)); return points; }
internal void onArrowSplit(bool added, int idx, int cnt) { if (added) { foreach (Attachment attc in attachments) { switch (attc.type) { case AttachTo.ArrowPoint: { int point = attc.attData; if (point >= idx) attc.attData += cnt; } break; case AttachTo.ArrowSegment: { int segment = attc.attData; if (segment >= idx) attc.attData += cnt; } break; } } // for } else { foreach (Attachment attc in attachments) { switch (attc.type) { case AttachTo.ArrowPoint: { int point = attc.attData; if (point >= idx) { if (point < idx + cnt) attc.attData = idx; else attc.attData -= cnt; } } break; case AttachTo.ArrowSegment: { int segment = attc.attData; if (segment >= idx) { if (segment < idx + cnt) attc.attData = idx - 1; else attc.attData -= cnt; if (attc.attData < 0) attc.attData = 0; } } break; } } // for } prevPoints = ((Arrow)mainObj).Points.Clone(); }
public PointCollection Clone() { PointCollection copy = new PointCollection(0); foreach (PointF pt in InnerList) copy.Add(pt); return copy; }
/// <summary> /// Has to be called by the main object when its state is restored due to undo/redo. /// </summary> internal void onRestoreState() { if (mainObj.getType() == ItemType.Arrow) { prevPoints = ((Arrow)mainObj).Points.Clone(); } else { prevRect = mainObj.getBoundingRect(); prevRotation = (mainObj as Node).rotation(); } }
/// <summary> /// Converts PointCollection object into point array /// </summary> /// <param name="ptc">PointCollection to be converted</param> /// <returns>Corresponded point array</returns> PointF[] PtColl2Pts(PointCollection ptc) { PointF[] pts = null; try { pts = new PointF[ptc.Count]; for (int i=0;i<ptc.Count;i++) { pts[i]=ptc[i]; pts[i].Y = m_FlowChart.DocExtents.Height - ptc[i].Y; } } catch ( Exception ex) { m_status = ex.Message; Trace.WriteLine(String.Format("{0} error {1}\n","DxfHelper.PtColl2Pts",ex.Message)); } Trace.WriteLine(String.Format("PtColl2Pts {0} -> {1}", ptc.Count, pts.Length)); return pts; }
internal override PointCollection getOutlinePoly() { RectangleF br = BoundingRect; PointCollection points = new PointCollection(0); GraphicsPath gp = null; switch (style) { case BoxStyle.Rectangle: case BoxStyle.RoundedRectangle: points.Add(new PointF(br.Left, br.Top)); points.Add(new PointF(br.Right, br.Top)); points.Add(new PointF(br.Right, br.Bottom)); points.Add(new PointF(br.Left, br.Bottom)); break; case BoxStyle.Shape: gp = Shape.getPath(shapeData, rotation()); gp.Flatten(); points.AddRange(gp.PathPoints); gp.Dispose(); break; case BoxStyle.Ellipse: points = Utilities.approxEllipse(br, 40); break; case BoxStyle.Rhombus: // calculate the vertex coordinates points.Add(new PointF((br.Left + br.Right) / 2, br.Top)); points.Add(new PointF(br.Right, (br.Top + br.Bottom) / 2)); points.Add(new PointF((br.Left + br.Right) / 2, br.Bottom)); points.Add(new PointF(br.Left, (br.Top + br.Bottom) / 2)); break; case BoxStyle.Delay: gp = Utilities.getDelaySymbol(br); gp.Flatten(); points.AddRange(gp.PathPoints); gp.Dispose(); break; } return points; }