0

Hello stackoverflow!

I am attempting to fill an ellipse with a LinearGradientBrush to "simulate" the dark and sun "sides" of a planet. Variable ovalCenter is the 3D vector (System.Numerics) of the position of the planet with respect to the Sun. The method FBody.Unit returns a unit vector.

I compute the darkest point on the edge of the oval and (facing directly away from the sun) the lightest point on the edge of the oval (directly facing sun). These points are stored in darkSidePoint and sunSidePoint respectively.

I have checked the values of X, Y, and Z of these vectors and know they are correct.

I set a color for the "dark point" to Black and the color of the "light point" to the specified color of the planet. These colors are stored in darkSideColor and sunSideColor respectively.

I then created PointF structures for the "dark point" and the "light point" (darkSide and sunSide).

Given the start and end PointF structures and the colors, I create a LinearGradientBrush (bl).

I use the LinearGradientBrush and the ovalRect in calling Graphics.FillEllipse().

Here is the code:

Vector3 u = FBody.Unit(ovalCenter);
Vector3 fromBodyCenterToEdge = u * ovalRect.Width / 2.0f;
Vector3 darkSidePoint = ovalCenter + fromBodyCenterToEdge;
Vector3 sunSidePoint = ovalCenter - fromBodyCenterToEdge;

PointF darkSide = new PointF(darkSidePoint.X, darkSidePoint.Y);
PointF sunSide = new PointF(sunSidePoint.X, sunSidePoint.Y); 
Color darkSideColor = Color.Black;
Color sunSideColor = penBody.Color;

LinearGradientBrush bl = new LinearGradientBrush(darkSide, sunSide, darkSideColor, sunSideColor);

g.FillEllipse(bl, ovalRect);

My expectation was that the oval would be filled and appear with a gradient fill that started in Black at "dark point" and transition smoothly to "Body Color" (for example, Brown) and look something like this:

enter image description here

Instead, I see something like this:

enter image description here

Any thoughts from the community on what I am doing wrong?

The following are the variable values (from Watch, in VS2022):

enter image description here

I followed guidance (as best I could understand) from MSDN on-line help regarding LinearGradientBrush.

2 Answers 2

1

From a "debug" point of view, you need to display the "graphics values" (e.g. points) you're generating. The values of the different elements are related; and just showing code doesn't provide any insight.

Sign up to request clarification or add additional context in comments.

2 Comments

Additionally, this is the sort of pattern you get when the two points are really close to eachother and/or negative. Double and then triplecheck darkSide and sunSide, they should be in pixel coordinate space and be quite far apart. Your sreenshots look like they are -really- close together, < 1.0 to be precise.
Excellent questions/comments. I have modified the question to include Watch variable values. As implied above, it could be that this issue is a result of Graphics scale/rotate transforms (not shown in my code as several methods are operating in the context of those transforms). I had not run into issues on this (until) now. Perhaps I should transform to local/device coordinates first?
0

With thanks to #Paul-Jan, the issue was the presence of scaling/transform of the Graphics context. The code was changed as below and appears to have resolved the issue:

Vector3 u = FBody.Unit(bodyCenter);
float bodyRadius = (float)Math.Sqrt(2.0) * bodyRect.Width / 2.0f;
Vector3 fromBodyCenterToEdge = u * bodyRadius;
Vector3 darkSidePoint = bodyCenter + fromBodyCenterToEdge;
Vector3 sunSidePoint = bodyCenter - fromBodyCenterToEdge;

PointF darkestPt = new PointF(darkSidePoint.X, darkSidePoint.Y);
PointF brightestPt = new PointF(sunSidePoint.X, sunSidePoint.Y); 
Color darkColor = Color.Black;
Color brightColor = penBody.Color;

/*
 * added/modified code to address gradient fill issues with transformed Graphics context
 */

PointF[] deviceColorPts = [darkestPt, brightestPt];
g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.World, deviceColorPts);

PointF[] deviceBodyRectCorners = [bodyRect.Location, new PointF(bodyRect.Right, bodyRect.Bottom)];
g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.World, deviceBodyRectCorners);

GraphicsState gs = g.Save();
g.ResetTransform();

LinearGradientBrush bl = new LinearGradientBrush(deviceColorPts[0], deviceColorPts[1], darkColor, brightColor);

RectangleF deviceBodyRect = new RectangleF(deviceBodyRectCorners[0], new SizeF(deviceBodyRectCorners[1].X - deviceBodyRectCorners[0].X,
                                                                                         deviceBodyRectCorners[1].Y - deviceBodyRectCorners[0].Y));

g.FillEllipse(bl, deviceBodyRect);

g.Restore(gs);

These changes resulted in nice results:

enter image description here

My thanks!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.