public VariableVector GetBestConnector(VariableVector farAway) { // Current algorithm: There are 360°/_connectors equal-sized sectors; // the "best connector" is the intersection of a sector center line // nearest to the line from center to farAway. // Other ideas: // - Divide the circumference of the box into equal-sized lengths. // - Like before, but with guaranteed connectors at corners. // - Like before, but with additional guaranteed connectors at edge midpoints. var result = new VariableVector("Connector", _solver); UnidirectionalComputationConstraint.CreateUnidirectionalComputationConstraint(input: new[] { farAway.X, farAway.Y, _center.X, _center.Y, _diagonal.X, _diagonal.Y }, output: new[] { result.X, result.Y }, computation: (input, output) => { // float[] inputValues = _input.Select(v => (float) v.Value.Lo).ToArray(); // ... I ignore "input" here and use the fields and local variables ... well ... ... should work ... VectorF centerF = _center.AsVectorF(); VectorF diagonalF = _diagonal.AsVectorF(); var farAwayF = farAway.AsVectorF(); VectorF d = farAwayF - centerF; double angle = Math.Atan2(d.GetY(), d.GetX()); double roundedAngle = NormalizedAngle(Math.Round(angle / _sectorAngle) * _sectorAngle); double diagX = diagonalF.GetX() / 2; double diagY = diagonalF.GetY() / 2; double diagonalAngle = NormalizedAngle(Math.Atan2(diagY, diagX)); double x, y; if (roundedAngle < diagonalAngle) { x = diagX; y = x * Math.Tan(roundedAngle); } else if (roundedAngle < Math.PI - diagonalAngle) { y = diagY; x = y * Math.Tan(Math.PI / 2 - roundedAngle); } else if (roundedAngle < Math.PI + diagonalAngle) { x = -diagX; y = x * Math.Tan(roundedAngle); } else if (roundedAngle < 2 * Math.PI - diagonalAngle) { y = -diagY; x = y * Math.Tan(Math.PI / 2 - roundedAngle); } else { x = diagX; y = x * Math.Tan(roundedAngle); } result.X.Set(centerF.GetX() + x); result.Y.Set(centerF.GetY() + y); }); return(result); }
private static void DrawText(Graphics graphics, string text, Font textFont, Color textColor, VectorF textCenter, Matrix m) { StringFormat centered = new StringFormat { Alignment = StringAlignment.Center }; float halfFontHeight = textFont.GetHeight() / 2; PointF position = (textCenter + new VectorF(0, halfFontHeight, "font/2 " + halfFontHeight)).AsMirroredPointF(); if (DEBUG) { graphics.FillEllipse(new SolidBrush(Color.Red), textCenter.AsMirroredPointF().X - 3, textCenter.AsMirroredPointF().Y - 3, 6, 6); graphics.DrawString(text, textFont, new SolidBrush(Color.LightGray), position, centered); } GraphicsContainer containerForTextPlacement = graphics.BeginContainer(); graphics.MultiplyTransform(m); graphics.DrawString(text, textFont, new SolidBrush(textColor), position, centered); graphics.EndContainer(containerForTextPlacement); }
public void Draw(Graphics graphics, StringBuilder htmlForClicks) { VectorF tailF = _tail.AsVectorF(); VectorF headF = _head.AsVectorF(); VectorF textBoxF = _textBox.AsVectorF(); float fWidth = (float)_width; PointF tailPoint = tailF.AsMirroredPointF(); PointF headPoint = headF.AsMirroredPointF(); if (tailPoint != headPoint) { float absoluteArrowSize = Math.Min(10 * fWidth, (headF - tailF).Length() / 4); float relativeArrowSize = absoluteArrowSize / fWidth; //Console.WriteLine(_name + ">S=" + relativeArrowSize); var pen = new Pen(_color, fWidth) { StartCap = LineCap.RoundAnchor, // arrowsize is relative to line width, therefore we divide by fWidth CustomEndCap = new AdjustableArrowCap(relativeArrowSize / 2, relativeArrowSize, isFilled: false) }; graphics.DrawLine(pen, tailPoint, headPoint); } else { graphics.FillEllipse(new SolidBrush(_color), headPoint.X - fWidth / 2, headPoint.Y - fWidth / 2, fWidth, fWidth); } if (DEBUG) { graphics.FillEllipse(new SolidBrush(Color.GreenYellow), headPoint.X - fWidth / 2, headPoint.Y - fWidth / 2, fWidth, fWidth); graphics.FillEllipse(new SolidBrush(Color.Aqua), tailPoint.X - fWidth / 2, tailPoint.Y - fWidth / 2, fWidth, fWidth); } VectorF textCenterF = _textLocation >= 0 ? tailF * (1 - _textLocation) + headF * _textLocation : tailF + (headF - tailF).Unit() * -_textLocation; float halfTextWidth = textBoxF.GetX() / 2; float lineAngleDegrees = (float)(-Math.Atan2(headF.GetY() - tailF.GetY(), headF.GetX() - tailF.GetX()) * 180 / Math.PI); var textTransform = new Matrix(); switch (_lineTextPlacement) { case LineTextPlacement.Left: textTransform.Translate(-halfTextWidth, 0); break; case LineTextPlacement.Center: break; case LineTextPlacement.Right: textTransform.Translate(halfTextWidth, 0); break; case LineTextPlacement.LeftInclined: textTransform.RotateAt(lineAngleDegrees, textCenterF.AsMirroredPointF()); textTransform.Translate(-halfTextWidth, 0); break; case LineTextPlacement.CenterInclined: textTransform.RotateAt(lineAngleDegrees, textCenterF.AsMirroredPointF()); break; case LineTextPlacement.RightInclined: textTransform.RotateAt(lineAngleDegrees, textCenterF.AsMirroredPointF()); textTransform.Translate(halfTextWidth, 0); break; default: throw new ArgumentOutOfRangeException(); } // Move text away from line; needs improvement for steeply inclined lines textTransform.Translate(0, -textBoxF.GetY() / 3); DrawText(graphics, _text, _textFont, _textColor, textCenterF, textTransform); if (!string.IsNullOrWhiteSpace(_edgeInfo)) { WriteToolTipHtml(graphics, htmlForClicks, _edgeInfo, tailPoint, headPoint); } }
public void Draw(Graphics graphics, StringBuilder htmlForClicks) { var diagonalF = _diagonal.AsVectorF(); var textBoxF = _textBox.AsVectorF(); var centerF = _center.AsVectorF(); VectorF upperLeftF = _center.AsVectorF() - ~diagonalF / 2; FillBox(graphics, new SolidBrush(_borderColor), upperLeftF.GetX(), -upperLeftF.GetY(), diagonalF.GetX(), diagonalF.GetY()); VectorF borderDiagonalF = new VectorF(_borderWidth, _borderWidth, "45°@" + _borderWidth); VectorF upperLeftInnerF = upperLeftF + ~borderDiagonalF; VectorF diagonalInnerF = diagonalF - 2 * borderDiagonalF; FillBox(graphics, new SolidBrush(_color), upperLeftInnerF.GetX(), -upperLeftInnerF.GetY(), diagonalInnerF.GetX(), diagonalInnerF.GetY()); Matrix m = new Matrix(); switch (_boxTextPlacement) { case BoxTextPlacement.Left: m.Translate(-(diagonalF - textBoxF).GetX() / 2, 0); break; case BoxTextPlacement.Center: break; case BoxTextPlacement.Right: m.Translate((diagonalF - textBoxF).GetX() / 2, 0); break; case BoxTextPlacement.LeftUp: m.Translate(0, -(diagonalF - textBoxF).AsMirroredPointF().Y / 2); m.RotateAt(-90, centerF.AsMirroredPointF()); break; case BoxTextPlacement.CenterUp: m.RotateAt(-90, centerF.AsMirroredPointF()); break; case BoxTextPlacement.RightUp: m.Translate(0, (diagonalF - textBoxF).AsMirroredPointF().Y / 2); m.RotateAt(-90, centerF.AsMirroredPointF()); break; case BoxTextPlacement.LeftDown: m.Translate(0, (diagonalF - textBoxF).AsMirroredPointF().Y / 2); m.RotateAt(90, centerF.AsMirroredPointF()); break; case BoxTextPlacement.CenterDown: m.RotateAt(90, centerF.AsMirroredPointF()); break; case BoxTextPlacement.RightDown: m.Translate(0, -(diagonalF - textBoxF).AsMirroredPointF().Y / 2); m.RotateAt(90, centerF.AsMirroredPointF()); break; default: throw new ArgumentOutOfRangeException(nameof(_boxTextPlacement), _boxTextPlacement, null); } var lineHeight = new VectorF(0, _textFont.GetHeight() * 1.1f, "lineHeight"); VectorF textLocation = centerF + new VectorF(0, lineHeight.GetY() / 2 * (_text.Length - 1), "textOffset"); for (int i = 0; i < _text.Length; i++, textLocation -= lineHeight) { DrawText(graphics, _text[i], _textFont, _textColor, textLocation, m); } if (_htmlRef != null) { VectorF lowerRightF = _center.AsVectorF() + ~diagonalF / 2; WriteBoxHtml(graphics, htmlForClicks, _htmlRef, upperLeftF.AsMirroredPointF(), lowerRightF.AsMirroredPointF(), _text.Length > 0 ? _text[0] : ""); } }