Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I create squircle (not rounded rectangle)? #7023

Closed
difhel opened this issue Mar 17, 2023 · 7 comments
Closed

How can I create squircle (not rounded rectangle)? #7023

difhel opened this issue Mar 17, 2023 · 7 comments

Comments

@difhel
Copy link

difhel commented Mar 17, 2023

I want to put squircle to my image. A squircle is a shape intermediate between a square and a circle.
image
The task I am doing is to generate image from my Figma layout:
temp
In this case squircle is rounded rectangle with corner smoothing. It has following parameters: corner radius (in px) and corner smothing (from 0 to 1).
image
The existing rounded_rectangle function cannot be used because it does not have a corner smoothing parameter.

def rounded_rectangle(self, xy, radius=0, fill=None, outline=None, width=1):

How can I do it?

@radarhere
Copy link
Member

Just to note - the diagram at the beginning of your post comes from https://en.wikipedia.org/wiki/Squircle.

I initially wondered in this could be done using ImageDraw.chord(), but I don't think so.

Instead, I found https://www.codespeedy.com/implement-a-superellipse-in-python/, which used matplotlib and NumPy.

I presume you're already using other parts of Pillow, so here is code that creates the plot, saves it in memory as an image, rereads that image with Pillow, and creates an animated GIF from the different values.

import matplotlib.pyplot as plt
import numpy as np
import io
from PIL import Image
a = 5
b = 4
t = np.linspace(0, 2 * np.pi, 100)
ims = []
for n in range(2, 10):
	x = ((np.abs(np.cos(t))) ** (2 / n)) * a * np.sign(np.cos(t))
	y = ((np.abs(np.sin(t))) ** (2 / n)) * b * np.sign(np.sin(t))
	plt.axis("off")
	plt.plot(x, y)
	with io.BytesIO() as f:
		plt.savefig(f, bbox_inches="tight")
		im = Image.open(f)
		im.load()
		ims.append(im)
	plt.clf()
ims[0].save("squircle.gif", save_all=True, append_images=ims[1:], disposal=1, duration=350, loop=0)

squircle

@difhel
Copy link
Author

difhel commented Mar 18, 2023

Thank you for your answer. Could you explain how the parameters in your code correlate with those in the screenshot in Figma? The thing is that I need to achieve a pixel-perfect implementation as on the layout.

@Yay295
Copy link
Contributor

Yay295 commented Mar 18, 2023

I found a blog post by Figma about their squircles. It seems like they use Bézier curves, so I don't believe the code radarhere posted will be an exact match. They don't specify their exact formula, but I think it could be determined by following what they talk about in that blog post (you can start at the section titled "Keep it simple, squircle").

@radarhere
Copy link
Member

I've found https://github.com/tienphaw/figma-squircle, which provides "Figma-flavored squircles for everyone" but "does not guarantee to produce the same results as you would get in Figma"

If a library dedicated to this purpose can't guarantee pixel perfect results, then I don't imagine that Figma has released enough information about their closed source software to make exact replication possible.

I thought about exporting the shape from Figma, but testing this with the CSS and SVG export methods, corner smoothing didn't change the output. You could export your squircle from Figma as a PNG, and then open and use that image in Pillow, either to paste or as a mask, but I suspect that wouldn't be satisfactory for your purposes.

@difhel
Copy link
Author

difhel commented Mar 21, 2023

Thanks for this git repository. It sounds like what I'm looking for. I wish I could do it with Pillow. I guess I'll be implementing it in JS then.
I also tried the idea of exporting a PNG-squircle before writing the issue. However, the height of my squircle depends on the text inside it, so you can't export the whole squircle. I tried exporting only the top of the squircle to substitute text with background fill under it, but got corners too small. It turns out that corners rounding also depends on the size of the squircle, so the option to export in any form (even SVG) is unlikely to work.

In any case, thanks for your help. I'll leave the issue open for a while and close it if I don't get more options.

@radarhere
Copy link
Member

"Figma corner smoothing" (or Figma squircles, or Figma superellipses) seems like very specific functionality to be a part of Pillow to my mind.

If you do want to try and approximate the functionality in Python, you could follow the documentation linked. Bezier curves are possible in matplotlib - https://matplotlib.org/stable/gallery/shapes_and_collections/quad_bezier.html

@nightadmin was there anything else, or can this be closed?

@difhel
Copy link
Author

difhel commented Apr 10, 2023

Thanks, I'll try JS Figma squircles realization.

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

No branches or pull requests

3 participants