Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Take the circumference of the inner circle. This is the circle you want the base of the characters to be rendered onto. We'll call this circumference <code>totalLength</code>.</p> <p>I assume you have a list of strings to render around the circle in <code>textItems</code>.</p> <p>Take the width of each string into a <code>textWidths</code> array and distribute them evenly across <code>totalLength</code>, perhaps like this pseudo(pythonish) code:</p> <pre><code>block = max(textWidths) assert(block * len(textWidths) &lt;= totalLength) offsets = [(block * i) + ((block-width) / 2) for i, width in enumerate(textWidths)] </code></pre> <p>Although better layouts can no doubt be done in the cases where the assert would trigger, all that really matters is that we know where individual words start and end in a known area. To render on a straight line of length <code>totalLength</code> we simply start rendering each block of text at <code>offsets[i]</code>.</p> <p>To get it onto the circle, we'll map that straight line back onto the circumference. To do that we need to map each pixel along that line onto a position on the circle and an angle. This function converts the offset along that line into an angle (it takes values in the range 0 to <code>totalLength</code>)</p> <pre><code>def offsetToAngle(pixel): ratio = pixel / totalLength angle = math.pi * 2 * ratio # cool kids use radians. return angle </code></pre> <p>that's your angle. To get a position:</p> <pre><code>def angleToPosition(angle, characterWidth): xNorm = math.sin(angle + circleRotation) yNorm = math.cos(angle + circleRotation) halfCWidth = characterWidth / 2 x = xNorm * radius + yNorm * halfCWidth # +y = tangent y = yNorm * radius - xNorm * halfCWidth # -x = tangent again. # translate to the circle centre x += circleCentre.x y += circleCentre.y return x,y </code></pre> <p>That's a bit more tricky. This is pretty much the crux of your issues, I'd have thought. The big deal is that you need to offset back along the tangent of the circle to work out the point to start rendering so that the middle of the character hits the radius of the circle. What constitues 'back' depends on your coordinate system. if 0,0 is in the bottom left, then the signs of the tangent components is swapped. I assumed top left.</p> <p><em>This is important:</em> I'm also making a big assumption that the text rotation occurs around the bottom left of the glyph. If it doesn't then things will look a bit weird. It will be more noticeable at larger font sizes. There is always a way to compensate for wherever it rotates around, and there's usually a way to tell the system where you want the rotation origin to be (that will be related to the <code>CGContextTranslateCTM</code> call in your code I'd imagine) you'll need to do a small experiment to get characters drawing at a single point rotating around their bottom left.</p> <p><code>circleRotation</code> is just an offset so you can rotate the whole circle, rather than having things always be in the same orientation. That's in radians too.</p> <p>so now for each character in each block of text:</p> <pre><code>for text, offset in zip(textItems, offsets): pix = offset # start each block at the offset we calculated earlier. for c in text: cWidth = measureGlyph(c) # choose the circumference location of the middle of the character # this is to match with the tangent calculation of tangentToOffset angle = offsetToAngle(pix + cWidth / 2) x,y = angleToPosition(angle, cWidth) drawGlyph(c, x, y, angle) pix += cWidth # start of next character in circumference space </code></pre> <p>That's the concept, anyway.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload