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.

@d-hedberg
Copy link

d-hedberg commented Dec 9, 2025

I find the graphic primitives provided by Modelica sufficient for creating the graphics needed for icons (including the possibility to use SVG images in Bitmap primitives). Seems a bit too drastic to deprecate something that has been around pretty much since the birth of Modelica and replace it with something new that doesn't bring that much more to the table. All tools would still have to support the original primitives for many years to come as many libraries depend on these, so tools would become more complex not only for the developers of the tools but also for the users.
Obviously there has been slight variations in the fill patterns in tools for years and I don't find this to be a critical issue that would justify deprecating the graphics annotations.

@HansOlsson
Copy link
Collaborator Author

And preliminarily for Dymola the model from #3789 (review) gives:

Package

@HansOlsson
Copy link
Collaborator Author

I have now tried to clarify the gradient instead of restricting it.
More work remain, but I think it is a step forward.

@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.

The gradient goes from line color to fill color.
Gradients are defined for the geometry of a \lstinline!GraphicItem! before its \lstinline!rotation! is applied.
The coloring is defined for an enclosing shape, and then clipped to the actual shape of the \lstinline!GraphicItem!.
For \lstinline!HorizontalCylinder! and \lstinline!VerticalCylinder!, the minimal enclosing axis-parallel rectangle is used, while \lstinline!Sphere! uses the smallest enclosing ellipse.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It should be clarified that we don't mean the less computationally tractable smallest-perimeter ellipse:

Suggested change
For \lstinline!HorizontalCylinder! and \lstinline!VerticalCylinder!, the minimal enclosing axis-parallel rectangle is used, while \lstinline!Sphere! uses the smallest enclosing ellipse.
For \lstinline!HorizontalCylinder! and \lstinline!VerticalCylinder!, the minimal enclosing axis-parallel rectangle is used, while \lstinline!Sphere! uses the smallest area enclosing ellipse.

@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

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