Hi, the title explains all, anyone knows how to fill a circle with horizontal text?
No text bending, just filling a circle with circle-shaped paragraph?
Fill a circle with text
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: Fill a circle with text
1. Make text with love.graphics.printf and limited width https://love2d.org/wiki/love.graphics.printf
2. Get the width and height of current font and text https://love2d.org/wiki/Font:getWidth
3. Use math to get the x, y and radius of circle.
- BrotSagtMist
- Party member
- Posts: 657
- Joined: Fri Aug 06, 2021 10:30 pm
Re: Fill a circle with text
like this, the use case for this is in case of having a round display (like zeblaze thor 5 smartwatch which runs Android not Android Wear)
- Attachments
Last edited by 9912 on Sun Jan 08, 2023 9:17 pm, edited 1 time in total.
Re: Fill a circle with text
Like this ?
If this is the case and you want centered multiline, you may have to look at love.graphics.printf or break the string for each "\n" and loop to display each line centered. (I didn't quite understand the thing requested, but by counting the number of characters with this technique, maybe we can have exactly the same result as in the picture). To tell you the truth I have never tried either but if you need help with that don't hesitate to ask
Edit: Maybe this library could be useful: SYSL-Text.
Code: Select all
function drawCircleText(text,x,y,r)
local font = love.graphics.getFont()
local text_w = font:getWidth(text)
local text_h = font:getHeight()
love.graphics.circle("line", x,y,r)
love.graphics.print(text, x, y, nil, nil, nil, text_w/2, text_h/2)
end
function love.draw()
drawCircleText(
"Hello",
love.graphics.getWidth()/2,
love.graphics.getHeight()/2,
50
)
end
Edit: Maybe this library could be useful: SYSL-Text.
Last edited by Bigfoot71 on Mon Jan 09, 2023 10:34 am, edited 1 time in total.
Re: Fill a circle with text
I quickly wrote a function that tries to do as in your image, it can be optimized, it is mainly to roughly show the principle described by darkfrei.
However to do exactly as in your picture I do not hide from you that it will be work.
Here's what it might look like:
You will notice that by removing the `ox` and `oy` parameters to printf you will be able to display it in a rectangle as well.
However to do exactly as in your picture I do not hide from you that it will be work.
Code: Select all
function drawCircleText(text,x,y,width)
local font = love.graphics.getFont()
local _, wrapped_text = font:getWrap(text, width)
local height = #wrapped_text * font:getHeight()
local r = (width > height and width or height) * .6 -- I do not divide tails by two so that the text is not tails at the edge of the circle
love.graphics.circle("line", x,y,r)
--love.graphics.rectangle("line", x, y, width, height)
love.graphics.printf(text, x, y, width, "center", nil, nil, nil, width/2, height/2)
end
function love.draw()
drawCircleText(
"Lörem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
love.graphics.getWidth()/2,
love.graphics.getHeight()/2,
150
)
end
You will notice that by removing the `ox` and `oy` parameters to printf you will be able to display it in a rectangle as well.
Re: Fill a circle with text
I've made this; it was an interesting challenge. While working on it I found what appears to be a bug in love's text justification code, so I wrote my own.
It's expensive, so try to not call it every frame. It returns a Text object that you can draw at the centre of the circle.
Code: Select all
local function genRoundText(text, font, radius, align, topMargin)
local words, nwords
if align == 'justify' then
words = {}
nwords = 0
end
local fontHeight = font:getHeight()
if topMargin == 0 then
topMargin = fontHeight
end
local textObject = love.graphics.newText(font)
local y = -radius + topMargin
while text ~= "" and math.abs(y) < radius do
local ldist = math.floor(math.min(math.sqrt(radius^2 - y^2),
math.sqrt(radius^2 - (y + fontHeight)^2)))
local width, wrapped = font:getWrap(text, ldist * 2)
local line = wrapped[1]
if align == 'justify' then
if wrapped[2] then
-- Love's justify alignment is not working properly. Use our own algo.
local total = 0
for word in string.gmatch(line, '[^ ]+') do
nwords = nwords + 1
words[nwords] = word
total = total + font:getWidth(word)
end
local to_distribute = ldist * 2 - total
local x = 0
local current = 0
for i = 1, nwords do
local ww = font:getWidth(words[i])
textObject:add(words[i], -ldist + x, y)
if i ~= nwords then
local old = current
current = math.floor(to_distribute * i / (nwords - 1))
x = x + ww + (current - old)
end
end
-- clear table for next line
for i = nwords, 1, -1 do
words[i] = nil
end
nwords = 0
else
align = 'left'
end
end
if align ~= 'justify' then
textObject:addf(wrapped[1], ldist*2, align, -ldist, y)
end
y = y + fontHeight
text = text:sub(#wrapped[1] + 1)
end
return textObject
end
local font = love.graphics.setNewFont(27)
local txt = genRoundText("Lorem ipsum dolor sit amet"
.. " enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis."
.. " Maecenas malesuada elit lectus felis, malesuada ultrices. Curabitur"
.. " et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo"
.. " volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor."
.. " Pellentesque facilisis. Nulla imperdiet sit amet magna. Vestibulum"
.. " dapibus, mauris nec malesuada fames ac turpis velit, rhoncus eu, luctus"
.. " et interdum adipiscing wisi. Aliquam erat ac ipsum. Integer"
, font, 290, 'justify', 30)
function love.draw()
love.graphics.draw(txt, 400, 300, 0, 1, 1, 0, 0)
love.graphics.circle("line", 400, 300, 290)
end
Re: Fill a circle with text
Wow, super cool! I wanted to understand your approach to the problem, so I restructured it a bit by commenting on the passages.
That way, if someone else like me wants to try to understand this code I will have tried to chew the work a bit (hoping that my retranslation of the comments is good to avoid misunderstandings)
Code: Select all
-- Function by pgimeno --
local function genRoundText(text, font, radius, align, topMargin)
-- Word table pre-declaration (see https://love2d.org/forums/viewtopic.php?p=252702#p252702)
local words, nwords
if align == 'justify' then
words = {}
nwords = 0
end
-- If topMargin is 0, use default font height
local fontHeight = font:getHeight()
if topMargin == 0 then
topMargin = fontHeight
end
-- Initializes an empty text object
local textObject = love.graphics.newText(font)
-- Positions the initial y position of the text
local y = -radius + topMargin
-- Keep adding text until all given text is used
-- or the position y exceeds the radius of the circle
while text ~= "" and math.abs(y) < radius do
-- Calculation of the maximum distance between the text
-- and the center of the circumference taking into account
-- the high and low extremities of the current line of text.
local ldist = math.floor(
math.min(
math.sqrt(radius^2 - y^2),
math.sqrt(radius^2 - (y + fontHeight)^2)
)
)
-- Wraps the text to the calculated maximum distance
local _, wrapped = font:getWrap(text, ldist * 2)
local line = wrapped[1]
-- If the alignment is "justify", use our own justification feature
if align == 'justify' then
-- Checks if the line has been wrapped (if it is too long to fit
-- within the calculated maximum distance)
if wrapped[2] then
-- Break the line into words
-- and calculates the total distance occupied by the words
local total = 0
for word in string.gmatch(line, '[^ ]+') do
nwords = nwords + 1
words[nwords] = word
total = total + font:getWidth(word)
end
-- Calculates the distance to distribute between the words
local to_distribute = ldist * 2 - total
local x = 0
local current = 0
-- Adds each word to the text object
-- adjusting the spacing between them to justify the text
for i = 1, nwords do
local ww = font:getWidth(words[i])
textObject:add(words[i], -ldist + x, y)
if i ~= nwords then
local old = current
current = math.floor(to_distribute * i / (nwords - 1))
x = x + ww + (current - old)
end
end
-- Clear table for next line
for i = nwords, 1, -1 do
words[i] = nil
end
nwords = 0
else
align = 'left' -- If the line has not been wrapped, use the specified alignment
end
end
-- If the alignment is not "justify", use the default alignment
if align ~= 'justify' then
textObject:addf(wrapped[1], ldist*2, align, -ldist, y)
end
-- Advance the y position for the next line
y = y + fontHeight
text = text:sub(#wrapped[1] + 1) -- Remove the used line from the remaining text string
end
return textObject -- Returns the created text object
end
Last edited by Bigfoot71 on Tue Jan 10, 2023 8:59 pm, edited 1 time in total.
Re: Fill a circle with text
*looks the entire thread after logging in*
~Oh, what i´ve done, i've started a war lmao.
looked all replies (specially pgimeno's code) and it works with some params changed
The main idea is providing a way to show text in a round display of 320x320, the params i've used in this case are:
genRoundText(text, font, love.graphics.getWidth()/2, 'justify', love.graphics.getWidth()/(2*0.8)),
i've put getWidth()/2 due to the drawing function takes reference from the centre instead of (0,0), and put 0,8 on the topMargin to display the text like a visual novel on round display.
Idea: make a pull request on https://github.com/tanema/talkies to provide an option to override printing function
Löve file attached.
~Oh, what i´ve done, i've started a war lmao.
looked all replies (specially pgimeno's code) and it works with some params changed
Code: Select all
-- Function by pgimeno --
local function genRoundText(text, font, radius, align, topMargin)
-- Initializes an empty text object
local textObject = love.graphics.newText(font)
-- If topMargin is 0, use default font height
local fontHeight = font:getHeight()
if topMargin == 0 then
topMargin = fontHeight
end
-- Positions the initial y position of the text
local y = -radius + topMargin
-- Keep adding text until all given text is used
-- or the position y exceeds the radius of the circle
while text ~= "" and math.abs(y) < radius do
-- Calculates the maximum left and right distance the text can occupy
-- using the distance from the origin (0,0) to the nearest point on the circle
local ldist = math.floor(math.min(math.sqrt(radius^2 - y^2),
math.sqrt(radius^2 - (y + fontHeight)^2)))
-- Wraps the text to the calculated maximum distance
local _, wrapped = font:getWrap(text, ldist * 2)
local line = wrapped[1]
-- If the alignment is "justify", use our own justification feature
if align == 'justify' then
-- Checks if the line has been wrapped (if it is too long to fit
-- within the calculated maximum distance)
if wrapped[2] then
-- Break the line into words
-- and calculates the total distance occupied by the words
local words = {}
local nwords = 0
local total = 0
for word in string.gmatch(line, '[^ ]+') do
nwords = nwords + 1
words[nwords] = word
total = total + font:getWidth(word)
end
-- Calculates the distance to distribute between the words
local to_distribute = ldist * 2 - total
local x = 0
local current = 0
-- Adds each word to the text object
-- adjusting the spacing between them to justify the text
for i = 1, nwords do
local ww = font:getWidth(words[i])
textObject:add(words[i], -ldist + x, y)
if i ~= nwords then
local old = current
current = math.floor(to_distribute * i / (nwords - 1))
x = x + ww + (current - old)
end
end
else
align = 'left' -- If the line has not been wrapped, use the default alignment
end
end
-- If the alignment is not "justify", use the default alignment
if align ~= 'justify' then
textObject:addf(wrapped[1], ldist*2, align, -ldist, y)
end
-- Advance the y position for the next line
y = y + fontHeight
text = text:sub(#wrapped[1] + 1) -- Remove the used line from the remaining text string
end
return textObject -- Returns the created text object
end
function love.conf(t)
t.console = true
end
love.window.updateMode(320,320)
local font = love.graphics.setNewFont(16)
local txt = genRoundText("Lorem ipsum dolor sit amet"
.. " enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis."
.. " Maecenas malesuada elit lectus felis, malesuada ultrices. Curabitur"
.. " et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo"
.. " volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor."
.. " Pellentesque facilisis. Nulla imperdiet sit amet magna. Vestibulum"
.. " dapibus, mauris nec malesuada fames ac turpis velit, rhoncus eu, luctus"
.. " et interdum adipiscing wisi. Aliquam erat ac ipsum. Integer"
, font, love.graphics.getWidth()/2, 'justify', love.graphics.getWidth()/1.6)
function love.draw()
love.graphics.draw(txt, love.graphics.getWidth()/2, love.graphics.getHeight()/2, 0, 1, 1, 0, 0)
love.graphics.circle("line", love.graphics.getWidth()/2, love.graphics.getHeight()/2, love.graphics.getWidth()/2)
end
genRoundText(text, font, love.graphics.getWidth()/2, 'justify', love.graphics.getWidth()/(2*0.8)),
i've put getWidth()/2 due to the drawing function takes reference from the centre instead of (0,0), and put 0,8 on the topMargin to display the text like a visual novel on round display.
Idea: make a pull request on https://github.com/tanema/talkies to provide an option to override printing function
Löve file attached.
- Attachments
-
- rounddisplay.love
- (1.6 KiB) Downloaded 72 times
Re: Fill a circle with text
I suggest to post the code snippet on a gist for future references or in a library.
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 1 guest