Conical Gradient Paint

Does anybody have a recommendation on what to use to paint a cone? Something that would paint an object like this:
image

This is what I'm trying to achieve and I found a ConicalGradientPaint that someone created and published, but it is not very efficient. The component's painting time goes up significantly when I use it.

image

Use CSS conic-gradient.
Here it's applied to a label with border radius set to 50% all around.

Copy and paste the following into a coordinate view.

Conic gradient label component.
[
  {
    "type": "ia.display.label",
    "version": 0,
    "props": {
      "style": {
        "borderStyle": "solid",
        "borderWidth": 1,
        "borderColor": "#493F3F",
        "borderTopLeftRadius": "50%",
        "borderTopRightRadius": "50%",
        "borderBottomLeftRadius": "50%",
        "borderBottomRightRadius": "50%",
        "height": 180,
        "background": "conic-gradient(from 230deg at 50% 50%, black, gray, white, black)"
      }
    },
    "meta": {
      "name": "ConicGradient"
    },
    "position": {
      "x": 616,
      "y": 87,
      "height": 180,
      "width": 180
    },
    "custom": {}
  }
]

Can I do that for a vision component? All the code I've been working with so far is Java code. I wouldn't know how to import a CSS object into my code.

Neither would I.
You really should have tagged your question as vision.

It is tagged as vision now

CSS is not a viable option for Vision, but that's not to say this can't be done. Is there a reason why this couldn't simply be made into an image and rendered with the image component?

Pointer? Or share your implementation of this code?

1 Like

This is the zip file that I downloaded from

ConicalGradientPaint.zip (1.0 MB)

Hmm. Pretty complicated. Not sure how to easily help.

A RelativeConicalGradientPaint from IA would be nice :grimacing:

Don't hold your breath.

Could you achieve an approximation of the effect you want in your screenshot with a regular linear gradient, but at an angle approximating your cone?

As in, consider the initial point outside the cone to the upper left, and the ending point offscreen to the lower right. I certainly won't look exactly right, but it would work :person_shrugging:

I took the code from your upload and an idea that I found on stack overflow, and I combined them to produce this in the paintable canvas:

from java.awt import RadialGradientPaint
from java.awt.geom import Point2D
g = event.graphics
center = Point2D.Float(50, 50)
radius = 50
fractions = [0.0, 1.0]
for degree in range(180):
	colorOne = system.gui.color(0 + degree, 0 + degree, 0 + degree)
	colorTwo = system.gui.color(50 + degree, 50 + degree, 50 + degree)
	colors = [colorOne, colorTwo]
	gradient = RadialGradientPaint(center, radius, fractions, colors)
	g.setPaint(gradient)
	g.fillArc(0, 0, 100, 100, degree, True)
for degree in range(180):
	colorOne = system.gui.color(181 - degree, 181 - degree, 181 - degree)
	colorTwo = system.gui.color(231 - degree, 231 - degree, 231 - degree)
	colors = [colorOne, colorTwo]
	gradient = RadialGradientPaint(center, radius, fractions, colors)
	g.setPaint(gradient)
	g.fillArc(0, 0, 100, 100, (degree + 180), True)

Here is the result:
image

It seems to be fairly close to what you are looking for. The script simply uses a standard radial gradient paint that is made progressively darker by adding an iteration index to the rgb values progressively for 180 degrees. Then it does the opposite for the next 180 degrees to create the dark to light conical effect.

7 Likes

This does work, however it looks a bit glitchy. The ConicalGradientPaint that I attached has a smoother look. I'll just keep using it for now.

Thanks everyone for your input.

I see what you mean. I quadrupled the size of it, and it looked like this:
image

Adding repaints smoothes it out, but I don't know how it compares in performance:

# four repaints
for _ in range(4):
	for degree in range(180):
	#[...]

image

3 Likes

Might be fun to try a "supersampling" technique, where you render to a BufferedImage at 4x resolution and then just scale that image down to render on the paintable canvas. Could cache that image pretty easily, too.

5 Likes

Experimenting with this idea, I found that there is no need to quadruple the resolution. Simply rendering the graphics using a buffered image at the same resolution cleans up the aliasing:
image

Revised Code:

from java.awt import RadialGradientPaint
from java.awt.geom import Point2D
from java.awt.image import BufferedImage

# Get the graphics object from the rePaint event
g = event.graphics

# Define the diameter of the cone
diameter = 400

# Define the location of the image
x = 0
y = 0

# Divide the diameter by 2 to get the radius
radius = diameter/2

# Create a Buffered image and a buffered graphics to eliminate aliasing
# This will be drawn by event.graphics
bufferedImage = BufferedImage(diameter, diameter, BufferedImage.TYPE_INT_ARGB)
bufferedGraphics = bufferedImage.createGraphics()

# Define the center of the buffered image by offsetting the x and y coordinates by the radius
center = Point2D.Float(x + radius, y + radius)

# Define fractions for color distribution in the gradient; the length must match the number of colors
# Any float from 0 to 1 is allowed representing a range of 0% to 100%
fractions = [0.0, 1.0]

# Iterate through all 360 degrees
for degree in range(360):

	# If the degree is less than half the circle, lighten each rgb value by 1 pixel
	if degree < 181:
		rgbOne = 0 + degree
		rgbTwo = 50 + degree
	
	# Once the degree is greater than half the circle, darken each rgb value by 1 pixel
	else:
		rgbOne = 360 - degree
		rgbTwo = 410 - degree
	
	# Apply the adjusted pixel to give the cone a directional lighting/shadow effect
	colorOne = system.gui.color(rgbOne, rgbOne, rgbOne)
	colorTwo = system.gui.color(rgbTwo, rgbTwo, rgbTwo)		
	
	# Put the colors into a list that must be the same length as the corresponding fractions list
	colors = [colorOne, colorTwo]
	
	# Create a radial gradient paint to simulate lighting/shading effects on the cone
	gradientPaint = RadialGradientPaint(center, radius, fractions, colors)
	
	# Assign the gradient paint to the buffferedGraphic's paint property
	bufferedGraphics.paint = gradientPaint
	
	# Fill a small arc segment to simulate a portion of the cone for the current degree
	# fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
	bufferedGraphics.fillArc(0, 0, diameter, diameter, degree, 1)

# Once all 360 degrees worth of arcs have been painted to the buffered image
# ...use the paintable canvas's graphics object to render the image
g.drawImage(bufferedImage, 0, 0, diameter, diameter, None)

Edit: added proper code comments, and did some minor refactoring to improve code clarity

7 Likes

Dumb question: what do you mean by copy and paste into a container? Wouldn't you need to create a label component first?

Roman, the code posted is a component. The third line declares it:

"type": "ia.display.label",

Bear in mind that I wrote it as a Perspective solution before the OP revealed that s/he was using Vision.

I glanced over the responses so I may have missed some...

You can embed SVGs into the Image library. I would just save the SVG transistor gave you into an SVG file and add it to the image library. It should look good. I've yet to have an SVG render wrong from the image library.