Skip to content

Conversation

@HansOlsson
Copy link
Collaborator

Closes #1826

@HansOlsson HansOlsson requested a review from henrikt-ma December 4, 2025 08:19
Copy link
Collaborator

@maltelenz maltelenz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are libraries out there that use this, and it is clearly useful, so removing this feature is not reasonable.

Example usage of HorizontalCylinder on a Polygon:

Image
model Package
  annotation(
    Icon(
      coordinateSystem(extent = {{-100, -100}, {100, 100}}, grid = {10, 10}),
      graphics = {
        Polygon(origin = {0.248, 0.044}, lineColor = {56, 56, 56}, fillColor = {128, 202, 255}, fillPattern = FillPattern.Solid, points = {{99.752, 100}, {99.752, 59.956}, {99.752, -50}, {100, -100}, {49.752, -100}, {-19.752, -100.044}, {-100.248, -100}, {-100.248, -50}, {-90.248, 29.956}, {-90.248, 79.956}, {-40.248, 79.956}, {-20.138, 79.813}, {-0.248, 79.956}, {19.752, 99.956}, {39.752, 99.956}, {59.752, 99.956}}, smooth = Smooth.Bezier),
        Polygon(origin = {0, -13.079}, lineColor = {192, 192, 192}, fillColor = {255, 255, 255}, pattern = LinePattern.None, fillPattern = FillPattern.HorizontalCylinder, points = {{100, -86.921}, {50, -86.921}, {-50, -86.921}, {-100, -86.921}, {-100, -36.921}, {-100, 53.079}, {-100, 103.079}, {-50, 103.079}, {0, 103.079}, {20, 83.079}, {50, 83.079}, {100, 83.079}, {100, 33.079}, {100, -36.921}}, smooth = Smooth.Bezier),
        Polygon(origin = {0, -10.704}, lineColor = {113, 113, 113}, fillColor = {255, 255, 255}, points = {{100, -89.296}, {50, -89.296}, {-50, -89.296}, {-100, -89.296}, {-100, -39.296}, {-100, 50.704}, {-100, 100.704}, {-50, 100.704}, {0, 100.704}, {20, 80.704}, {50, 80.704}, {100, 80.704}, {100, 30.704}, {100, -39.296}}, smooth = Smooth.Bezier)
      }
    )
  );
end Package;

@henrikt-ma
Copy link
Collaborator

The best way to proceed might then be to let the tools that have implemented this describe what they do, so that we can see if the implementations have something in common that we can agree upon?

@henrikt-ma henrikt-ma removed their request for review December 4, 2025 11:17
@d-hedberg
Copy link

We have libraries using these fill patterns on polygons. Why would we restrict/remove something that clearly is useful?

@HansOlsson
Copy link
Collaborator Author

The best way to proceed might then be to let the tools that have implemented this describe what they do, so that we can see if the implementations have something in common that we can agree upon?

Ok, making a table based on what I assume:

Pattern Ellipse Rectangle Polygon
Solid etc Yes Yes Yes
HorizontalCylinder Dymola,WSM Dymola,WSM WSM
VerticalCylinder Dymola,WSM Dymola,WSM WSM
Sphere Dymola, Dymola, ?

It seems that the only potentially missing feature is spherical gradient for polygons, and it shouldn't be that difficult to support.
So the simplest is to just don't change anything.

@d-hedberg
Copy link

System Modeler also supports sphere fills on Polygons.

@d-hedberg
Copy link

System Modeler
image

@d-hedberg
Copy link

For sphere fill on polygons, System Modeler does:
// The center point is the center point of the polygon's bounding box.
// The radius is the distance from the polygon's center point to the vertex farthest away.

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Dec 5, 2025

Just wondering, but wouldn't it make sense if the center and radius of the Sphere gradient were defined in such a way that they transform consistently with the rest of the shape under Euclidean transformations? (A spherical gradient with circular level curves can't transform naturally when skewing or scaling differently in different directions.)

If the center is defined so that it transforms naturally with the shape, it is natural to define the radius as the distance from the center to the controlling point furtherest away (simpler math compared to the distance to the point furtherest away on the curve delimiting the shape).

One simple definition of the center is to use the origin in the local coordinates of the shape. This also has the advantage that the user can select where to put the center.

Another option is to define the center as the center of mass of the polygon with vertices at the controlling points (simpler math compared to the center of mass of the curve delimiting the shape). For @d-hedberg's example of an equilateral triangle in #3789 (comment), this would result in the same color at all three corners of the triangle, no matter how it is rotated.

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Dec 7, 2025

Please don't take #3789 (comment) as a concrete proposal; it has several drawbacks:

  • The idea of using the local origin will break appearance badly for existing use if the origin isn't near "the center".
  • There are many more centers of mass that could be sensible choices, even without adding the more numerically demanding ones to the list, so picking one of them would become an arbitrary design decision.
  • It would be disruptive for System Modeler users.
  • It would require implementation effort for Wolfram to comply with the specification.

One thing that I believe might help us reach agreement would be to introduce a gradientCenter attribute in FilledShape, which would be optional for Rectangle and Ellipse, defaulting to being at the obvious center of the shape. For Polygon it should be non-optional, but with a deprecated semantics of tool-specific behavior when being unspecified.

However, when thinking more about how to define the "gradient length", I realized that it wasn't clear to me how Sphere should be applied even to a non-circular Ellipse. Should it be A or B below?
ellipses

Interpretations:

  • A: Paint a circle as if it were a sphere, and then transform the painted object.
  • B: Paint a big circle as if it were a sphere, then transform the boundary and use it to clip the painted circle.

@HansOlsson
Copy link
Collaborator Author

Please don't take #3789 (comment) as a concrete proposal; it has several drawbacks:

  • The idea of using the local origin will break appearance badly for existing use if the origin isn't near "the center".
  • There are many more centers of mass that could be sensible choices, even without adding the more numerically demanding ones to the list, so picking one of them would become an arbitrary design decision.
  • It would be disruptive for System Modeler users.
  • It would require implementation effort for Wolfram to comply with the specification.

One thing that I believe might help us reach agreement would be to introduce a gradientCenter attribute in FilledShape, which would be optional for Rectangle and Ellipse, defaulting to being at the obvious center of the shape. For Polygon it should be non-optional, but with a deprecated semantics of tool-specific behavior when being unspecified.

However, when thinking more about how to define the "gradient length", I realized that it wasn't clear to me how Sphere should be applied even to a non-circular Ellipse. Should it be A or B below? ellipses

Yes, sphere-gradient and non-circles (including ellipses) is a problem. I say that "A", i.e., sphere-gradient on a circle stretched to make it an ellipse is the most intuitive - but I have no idea how to easily generalize it to polygons.

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Dec 8, 2025

If we are not sure exactly what to do, it might also help to take a look at SVG. A Modelica Sphere gradient would most closely resemble a <radialGradient> fill in SVG (a tool can insert more <step> elements to better approximate the reflection on a sphere):

(SVG makes the interpretation A in #3789 (comment).)

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Dec 8, 2025

Yes, sphere-gradient and non-circles (including ellipses) is a problem. I say that "A", i.e., sphere-gradient on a circle stretched to make it an ellipse is the most intuitive - but I have no idea how to easily generalize it to polygons.

I would say that you need something equivalent to an ellipse to define the gradient, and then you can clip it to any desired shape. For a Rectangle there aren't that many candidates for how to define the default ellipse (inscribed or circumscribed), but for a Polygon it gets nasty to define a default.

However, note that even if defining a default for the specification is a nasty problem, making the Sphere gradient ellipse a part of FilledShape (only required when using Sphere on a Polygon, and with deprecated semantics for not specifying it) means that we leave it to tools to just throw out a not too crazy ellipse when Sphere fill is selected for a Polygon, and then it is up to the user to adjust the ellipse to taste.

Edit: Note that in comparison to SVG, SVG has a generic gradientTransform attribute, meaning that their definition of a radial gradient can concentrate on defining the gradient applied to a circular shape. It is because we don't have anything corresponding to gradientTransform in Modelica that we need a "gradient outermost ellipse", and not just a "gradient outermost circle" as in SVG.

@henrikt-ma
Copy link
Collaborator

There are libraries out there that use this, and it is clearly useful, so removing this feature is not reasonable.

Do you also have an easy way to find applications of Sphere to Rectangle or Polygon?

@HansOlsson
Copy link
Collaborator Author

HansOlsson commented Dec 8, 2025

Looking more I realize that there are two additional issues with gradients, with relevant icons in MSL:

@HansOlsson
Copy link
Collaborator Author

There are libraries out there that use this, and it is clearly useful, so removing this feature is not reasonable.

Do you also have an easy way to find applications of Sphere to Rectangle or Polygon?

For Rectangle it is used for Modelica.Mechanics.Translational.Components.Mass, looking like this in Dymola 2026x:

Mass

Nice effect, but unfortunately without any clear relation to radial gradient - more like a combined vertical and horizontal gradient.

@henrikt-ma
Copy link
Collaborator

Nice effect, but unfortunately without any clear relation to radial gradient - more like a combined vertical and horizontal gradient.

Yes. First I thought you had implemented the generalization to draw rays with a color gradient, each ray starting at the center and ending at the periphery, but then I saw the lighter "X" pattern which proved me wrong.

@henrikt-ma
Copy link
Collaborator

  • Modelica.Mechanics.MultiBody.Parts.Body rectangles with rounded corners, I assume we just base the gradient on the unrounded case?

Sounds very reasonable to me.

@adeas31
Copy link
Member

adeas31 commented Dec 8, 2025

Can you made an example model of all cases which other tools can load and show how they render patterns.

This is how OpenModelica renders the example given here #3789 (review)
image

For Modelica.Mechanics.MultiBody.Parts.Body,

image

For Modelica.Mechanics.Translational.Components.Mass

image

@henrikt-ma
Copy link
Collaborator

This is more or less the model I used in #3789 (comment):

model EllipsePainting
  model A
    annotation(
      Diagram(
        coordinateSystem(extent = {{-150, -90}, {150, 90}})
      ),
      Icon(
        coordinateSystem(extent = {{-100, -100}, {100, 100}}),
        graphics = {
          Ellipse(fillColor = {255, 0, 0}, fillPattern = FillPattern.Sphere, extent = {{-100, -100}, {100, 100}})
        }
      )
    );
  end A;

  A a1 annotation(
      Placement(transformation(origin = {40, 80}, extent = {{-20, -60}, {20, 60}}))
    );
  annotation(
    preferredView = "diagram",
    Diagram(
      coordinateSystem(extent = {{10, 10}, {130, 150}}),
      graphics = {
        Ellipse(origin = {100, 80}, fillColor = {255, 0, 0}, fillPattern = FillPattern.Sphere, extent = {{-20, -60}, {20, 60}})
      }
    )
  );
end EllipsePainting;

@adeas31
Copy link
Member

adeas31 commented Dec 8, 2025

This is more or less the model I used in #3789 (comment):

model EllipsePainting
  model A
    annotation(
      Diagram(
        coordinateSystem(extent = {{-150, -90}, {150, 90}})
      ),
      Icon(
        coordinateSystem(extent = {{-100, -100}, {100, 100}}),
        graphics = {
          Ellipse(fillColor = {255, 0, 0}, fillPattern = FillPattern.Sphere, extent = {{-100, -100}, {100, 100}})
        }
      )
    );
  end A;

  A a1 annotation(
      Placement(transformation(origin = {40, 80}, extent = {{-20, -60}, {20, 60}}))
    );
  annotation(
    preferredView = "diagram",
    Diagram(
      coordinateSystem(extent = {{10, 10}, {130, 150}}),
      graphics = {
        Ellipse(origin = {100, 80}, fillColor = {255, 0, 0}, fillPattern = FillPattern.Sphere, extent = {{-20, -60}, {20, 60}})
      }
    )
  );
end EllipsePainting;

This one gives,

image

@HansOlsson
Copy link
Collaborator Author

This is more or less the model I used in #3789 (comment):

Dymola 2026x generates the following (which I like):

EllipsePainting

@HansOlsson
Copy link
Collaborator Author

And for Modelica.Mechanics.MultiBody.Parts.Body Dymola 2026x generates (which I also like, except I don't know if there's an optical illusion or something odd at the rounded corners):

Body

@henrikt-ma
Copy link
Collaborator

And for Modelica.Mechanics.MultiBody.Parts.Body Dymola 2026x generates (which I also like, except I don't know if there's an optical illusion or something odd at the rounded corners):

I zoomed in, and it is not an optical illusion; the level curves of equal color are bent near the left end of the cylinder. Makes you wonder what is going on behind that gray connector… Any similar effects on a rectangle without rounded corners?

@HansOlsson
Copy link
Collaborator Author

And for Modelica.Mechanics.MultiBody.Parts.Body Dymola 2026x generates (which I also like, except I don't know if there's an optical illusion or something odd at the rounded corners):

I zoomed in, and it is not an optical illusion; the level curves of equal color are bent near the left end of the cylinder. Makes you wonder what is going on behind that gray connector… Any similar effects on a rectangle without rounded corners?

There shouldn't be. I know what could cause it specifically for rounded corners, but didn't investigate it enough.

@henrikt-ma
Copy link
Collaborator

With so much variation between tools in the looks of these gradients, maybe we better just conclude that the specification failed pretty badly this time by introducing all these underspecified ways of filling shapes. Instead of seeking agreement about how extend and clarify the specification, isn't it better that we make a plan to deprecate all these underspecified fills, and instead spend our efforts on strengthening the support for SVG?

For example, these are all things that would seem more worthwhile standardizing:

  • Allow inline SVG images, instead of the current limitation to Bitmap with an external file.
  • Define variable replacement for SVG <text> elements.
  • Define how to use Modelica expressions in SVG attributes.
  • Define how to do DynamicSelect in SVG.
  • Allow the entire Icon.graphics or Diagram.graphics to be a single SVG document.

@HansOlsson HansOlsson requested a review from maltelenz December 15, 2025 09:08
HansOlsson and others added 3 commits December 16, 2025 16:48
Co-authored-by: Henrik Tidefelt <henrikt@wolfram.com>
@HansOlsson
Copy link
Collaborator Author

I have tried to reformulate based on this, but without introducing "band".

@henrikt-ma
Copy link
Collaborator

I have tried to reformulate based on this, but without introducing "band".

Sure, that seems workable.

Co-authored-by: Henrik Tidefelt <henrikt@wolfram.com>
Copy link
Collaborator

@henrikt-ma henrikt-ma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this looks very reasonable to me, but I'd like to leave it to others working closer to the graphical user interfaces to provide approving reviews.

@d-hedberg
Copy link

d-hedberg commented Dec 17, 2025

Sounds good to me, but wouldn't it make sense to include some visual examples complementing the text? It would decrease the risk of different (mis)interpretations of the text.

@henrikt-ma
Copy link
Collaborator

Sounds good to me, but wouldn't it make sense to include some visual examples complementing the text? It would decrease the risk of different (mis)interpretations of the text.

I can promise to provide one or two examples with tool-independent scalable vector graphics, but it would be most convenient for me to do this in a separate pull request initiated by me. With this promise, I suggest that we accept the present PR without examples.

@henrikt-ma
Copy link
Collaborator

Sounds good to me, but wouldn't it make sense to include some visual examples complementing the text? It would decrease the risk of different (mis)interpretations of the text.

I can promise to provide one or two examples with tool-independent scalable vector graphics, but it would be most convenient for me to do this in a separate pull request initiated by me. With this promise, I suggest that we accept the present PR without examples.

Here they are (but I made them three figures start with): #3802

Co-authored-by: Henrik Tidefelt <henrikt@wolfram.com>
@henrikt-ma
Copy link
Collaborator

Regarding @HansOlsson's comment, I wouldn't find this PR a satisfactory fix for #1826 if didn't clarify how to draw the combination of Rectangle and Sphere (already in use in Modelica.Mechanics.Translational.Components.Mass, as noted above). I would also find it weird to specify how to draw Sphere on a Polygon, while leaving the more important application to Rectangle unspecified.

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Jan 7, 2026

In case there are concerns regarding the implementation effort for the current proposal, I have some notes and observations to share:

  • For the geometries of Ellipse and Rectangle, it is trivial to find the minimal enclosing ellipse or axis-parallel rectangle (especially considering that these are determined before applying any rotation).
  • For the less trivial Polygon, the minimal enclosing axis-parallel rectangle is very easy to compute also when using smooth = Bezier, as the $x$ and $y$ coordinates can be treated separately, only requiring the maximization of a quadratic polynomial for each spline segment. (A less ambitious tool could say that this is a quality-of-implementation-thing, and ignore smooth, but why not do it properly when it is so easy?)
  • The remaining case is finding the minimal area ellipse enclosing a Polygon, and I have explored this problem with the help of Claude Code, see below.

About the minimal area ellipse enclosing a Polygon:

  • For smooth = None, this is a well-studied problem, and there exists an incremental method, Welzl's algorithm, for tracking the minimal area ellipse while adding one point at a time, with O(1) complexity for each addition. It seems to have been originally developed for finding the minimal circle, but has been generalized for finding the minimal area ellipse.
  • For smooth = Bezier, the following simple strategy turns out to converge very fast:
    • By only considering the Bezier spline end points, one obtains a "lower bound ellipse" using the same method as for smooth = None.
    • For each spline segment, compute the maximum value of the quadratic function defining the ellipse.
    • Drop Bezier splines found to be inside the lower bound ellipse.
    • An upper bound for the minimum area is obtained from the maximum value of the quadratic, providing a very nice stopping criterion.
    • Split the Bezier segment where the maximum value of the quadratic was obtained, resulting in a new spline segment endpoint which will increase the lower bound ellipse, and keep iterating.
  • The "hard work" is to get the well known incremental method for smooth = None up and running; extending it to smooth = Bezier is just a thin layer on top.
  • Claude Code was able to give me a C++ implementation for the above, completely free of external dependencies.
  • The algorithm above was verified by comparing it to a few completely independent methods based on convex optimization, and found to give consistent results.
  • The running time was in the order of a millisecond for shapes defined by hundreds of points, and less for the actual cases in the MSL.
  • If a millisecond still seems expensive, note that the computation is only needed once per actual Polygon primitive using Sphere; the result can be cached, and when the Polygon is rotated or otherwise transformed, one only needs to transform the ellipse.

@HansOlsson
Copy link
Collaborator Author

HansOlsson commented Jan 8, 2026

Regarding @HansOlsson's comment, I wouldn't find this PR a satisfactory fix for #1826 if didn't clarify how to draw the combination of Rectangle and Sphere (already in use in Modelica.Mechanics.Translational.Components.Mass, as noted above). I would also find it weird to specify how to draw Sphere on a Polygon, while leaving the more important application to Rectangle unspecified.

I still think the current state of the proposal is still a major step forward, and the intent wasn't to leave it unspecified - but only to leave room for different gradients for the time being, and for rectangle with spherical gradient allow both:

and

Basically it specifies how to handle rotations, which color is at the center (point or line) and border (and defines how to compute the border), and the obvious symmetry for horizontal/vertical, but doesn't specify the exact details of the gradient (see also the discussion about conical vs spherical in #1826 ).

@henrikt-ma
Copy link
Collaborator

henrikt-ma commented Jan 8, 2026

I don't think we have talked about the fact that also Polygon is used with Sphere in the MSL.

This is what the VoluminousWheel would look like according to the current proposal (there are three Polygon items stacked here, where the Sphere gradient is only used for the bottom item, while the top item uses HorizontalCylinder):
sphere-gradient-wheel

The following image is a verification that the Sphere fill indeed goes from the fillColor (which is the solid color of the triangle on top) to the lineColor (which is the solid color of the – supposedly minimum area – ellipse surrounding wheel shape):
sphere-gradient-wheel-verification

@HansOlsson
Copy link
Collaborator Author

HansOlsson commented Jan 8, 2026

I don't think we have talked about the fact that also Polygon is used with Sphere in the MSL.

This is what the VoluminousWheel would look like according to the current proposal (there are three Polygon items stacked here, where the Sphere gradient is only used for the bottom item, while the top item uses HorizontalCylinder):

That looks ok-ish.

Compared to HorizontalCylinder it goes to dark also at the outer edge of the wheel; so it more a balloon tire where that part is in shadow than a more usual cylindrical car tire with a sharp shadow. That is consistent with the polygon-outline that is rounded at the top.

@henrikt-ma
Copy link
Collaborator

Regarding @HansOlsson's comment, I wouldn't find this PR a satisfactory fix for #1826 if didn't clarify how to draw the combination of Rectangle and Sphere (already in use in Modelica.Mechanics.Translational.Components.Mass, as noted above). I would also find it weird to specify how to draw Sphere on a Polygon, while leaving the more important application to Rectangle unspecified.

I still think the current state of the proposal is still a major step forward, and the intent wasn't to leave it unspecified - but only to leave room for different gradients for the time being, and for rectangle with spherical gradient allow both:

A big problem with this would be that the only supported way of getting somewhat predictable results for a rectangle would be to use Polygon instead of Rectangle. It reminds me of some strange examples I've seen in the MSL, where a Polygon with smooth = Bezier is used to draw an ellipse (PipeWithScalarField is a prime example of this). I think we should move away from such non-canonical shape representations, not leave the specification in a state where it also pushes library authors to change Rectangle into Polygon.

@HansOlsson
Copy link
Collaborator Author

A big problem with this would be that the only supported way of getting somewhat predictable results for a rectangle would be to use Polygon instead of Rectangle. It reminds me of some strange examples I've seen in the MSL, where a Polygon with smooth = Bezier is used to draw an ellipse (PipeWithScalarField is a prime example of this). I think we should move away from such non-canonical shape representations, not leave the specification in a state where it also pushes library authors to change Rectangle into Polygon.

I can understand the general idea, but:

  • There are current users of Rectangles with that Sphere-gradient, that may be happy with the current look in Dymola (at least they haven't complained much) - so we need to investigate that first. In general I find the combination of Sphere-gradient and non-ellipses (rectangles and polygons) confusing, but I can understand that it sort of works for almost elliptical shapes (that are given as polygons).
  • I have no idea why someone used a number Bezier-Polygons instead of ellipses in PipeWithScalarField. Was it someone not realizing that a circle seen from the side is an ellipse, or was there some actual reason?

@henrikt-ma
Copy link
Collaborator

That looks ok-ish.

Compared to HorizontalCylinder it goes to dark also at the outer edge of the wheel; so it more a balloon tire where that part is in shadow than a more usual cylindrical car tire with a sharp shadow. That is consistent with the polygon-outline that is rounded at the top.

I am not saying it is a great-looking wheel, I am just showing what the current proposal would mean for this icon in its current state. In order to work on the looks – if anyone would feel the urge – a first step must be to improve the specification so that the icon designer at least can have a rough idea of what to expect when using different combinations of shapes and fills.

@HansOlsson
Copy link
Collaborator Author

That looks ok-ish.
Compared to HorizontalCylinder it goes to dark also at the outer edge of the wheel; so it more a balloon tire where that part is in shadow than a more usual cylindrical car tire with a sharp shadow. That is consistent with the polygon-outline that is rounded at the top.

I am not saying it is a great-looking wheel, I am just showing what the current proposal would mean for this icon in its current state. In order to work on the looks – if anyone would feel the urge – a first step must be to improve the specification so that the icon designer at least can have a rough idea of what to expect when using different combinations of shapes and fills.

And I was just analyzing the look to confirm that it actually makes a bit of sense (even if not "great"); and not just an accident.

@HansOlsson HansOlsson added this to the 2026-January milestone Jan 8, 2026
@HansOlsson
Copy link
Collaborator Author

Language group: Seems ok - shouldn't over-specify. SimulationX has somewhat similar gradient for wheel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

What is fillPattern=FillPattern.Sphere on Rectangle or Polygon?

5 participants