Greetings! I’m trying to implement a custom VisualElement where, given two distinct points, I can draw a line that “link” them up. The user should be able to move these points around and the line should always connect the points. As you can see in the figure, I’m able to position the points inside another VisualElement container just fine.
For the movement part, I’ve followed this example from Unity’s documentation: Unity - Manual: Create a drag-and-drop UI inside a custom Editor window. It works as expected, but I’m having difficulty with the line drawing part. Here’s how I’ve structured the code so far:
Point
public class Point : VisualElement
{
public Color Color;
public Color OutlineColor;
public float Radius;
public float OutlineThickness;
public Point(Color color, Color outlineColor, float radius, float outlineThickness)
{
name = "Point";
Color = color;
OutlineColor = outlineColor;
Radius = radius;
OutlineThickness = outlineThickness;
style.width = 2 * Radius;
style.height = 2 * Radius;
generateVisualContent += OnGenerateVisualContent;
}
private void OnGenerateVisualContent(MeshGenerationContext context)
{
Painter2D painter = context.painter2D;
painter.fillColor = OutlineColor;
painter.BeginPath();
painter.Arc(new Vector2(Radius, Radius), Radius + OutlineThickness, 0.0f, 360.0f);
painter.Fill();
painter.fillColor = Color;
painter.BeginPath();
painter.Arc(new Vector2(Radius, Radius), Radius, 0.0f, 360.0f);
painter.Fill();
}
}
As can be seen, the point itself is just an overlay drawn on-top of the VisualElement, positioned in the middle of it (the diameter is the same size as the VE’s width and height). For the Line, I have the following:
Line
public class Line: VisualElement
{
public Color StartPointColor { get; set; }
public Color EndPointColor { get; set; }
public Color OutlineColor { get; set; }
public float Radius { get; set; }
public float Thickness { get; set; }
public Point StartPoint;
public Point EndPoint;
private const string styleSheet = "UI/VisualElements/SliceViewer/LinkStyleSheet";
private const string styleClass = "line";
private const string startPointStyleClass = "startPoint";
private const string endPointStyleClass = "endPoint";
public Line()
{
styleSheets.Add(Resources.Load<StyleSheet>(styleSheet));
AddToClassList(styleClass);
generateVisualContent += OnGenerateVisualContent;
}
public void SetVisualElements()
{
StartPoint = new Point(StartPointColor, OutlineColor, Radius, Thickness);
EndPoint = new Point(EndPointColor, OutlineColor, Radius, Thickness);
StartPoint.AddManipulator(new DragAndDropManipulator(StartPoint));
EndPoint.AddManipulator(new DragAndDropManipulator(EndPoint));
StartPoint.AddToClassList(startPointStyleClass);
EndPoint.AddToClassList(endPointStyleClass);
Add(StartPoint);
Add(EndPoint);
}
private void OnGenerateVisualContent(MeshGenerationContext context)
{
Painter2D painter = context.painter2D;
painter.strokeColor = OutlineColor;
painter.lineWidth = Thickness;
painter.lineJoin = LineJoin.Miter;
painter.lineCap = LineCap.Butt;
// PROBLEM HERE
painter.BeginPath();
painter.MoveTo(???); // StartPoint position
painter.LineTo(???); // EndPoint position
painter.Stroke();
}
public new class UxmlFactory : UxmlFactory<Line, UxmlTraits> { }
public new class UxmlTraits : VisualElement.UxmlTraits
{
UxmlColorAttributeDescription startPointColor = new UxmlColorAttributeDescription { name = "start-point-color", defaultValue = Color.red };
UxmlColorAttributeDescription endPointColor = new UxmlColorAttributeDescription { name = "end-point-color", defaultValue = Color.blue };
UxmlColorAttributeDescription outlineColor = new UxmlColorAttributeDescription { name = "outline-color", defaultValue = Color.white };
UxmlFloatAttributeDescription radius = new UxmlFloatAttributeDescription { name = "radius", defaultValue = 10.0f };
UxmlFloatAttributeDescription thickness = new UxmlFloatAttributeDescription { name = "thickness", defaultValue = 3.0f };
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
Line line = ve as Line;
line.Clear();
line.name = "Line";
line.StartPointColor = startPointColor.GetValueFromBag(bag, cc);
line.EndPointColor = endPointColor.GetValueFromBag(bag, cc);
line.OutlineColor = outlineColor.GetValueFromBag(bag, cc);
line.Radius = radius.GetValueFromBag(bag, cc);
line.Thickness = thickness.GetValueFromBag(bag, cc);
line.SetVisualElements();
}
}
}
I believe I can accomplish the drawing portion by stroking a line from the StartPoint location to the EndPoint location. My question is: how can I retrive their positions relative to their parent (the black box container)? I tried using their transform.position but it returns the zero vector. What I want is their position relative to how far they are positioned inside the parent element. And while we are at it, how can I trigger an event from within the child, so the parent can be notified and handle it? I imagine that’s how I could call MarkDirtyRepaint in the parent’s class, and have the line be redrawn.
Any help is much appretiated. Thank you all!