0

A picture is worth a thousand words...

enter image description here

I'd like to know how to be able to generate an image like that where (1) the two circles are obviously perfect circles, (2) I can define the beginning and ending parts of each region in terms of angles, e.g. section 1 starts at 0 radians from the vertical and ends at pi/2 radians from the vertical, etc., and (3) I can define the color of each region.

In fact the outside and inside of the ring should not have a black border; the border of each region should be the same color as each region.

How might I do this with, say, ImageMagick?

  • Presumably you would need to supply an inner and outer radius too? Is there just a single ring, or are you planning to later mention in passing that there may be other concentric rings you just didn't mention? ;-) – Mark Setchell Jul 29 '15 at 21:33

3 Answers3

0

You can create annulus in vector graphics with the Arc command. See Mozilla's Path Document for details on parameter.

With ImageMagick, you could -draw any part of an vector-graphic. Example:

convert -size 100x100 xc:transparent -stroke black -strokewidth 1 \
        -fill blue  -draw 'path "M 10 50 A 40 40 0 0 1 50 10 L 50 20 A 30 30 0 0 0 20 50 Z"' \
        -fill red   -draw 'path "M 50 10 A 40 40 0 0 1 90 50 L 80 50 A 30 30 0 0 0 50 20 Z"' \
        -fill green -draw 'path "M 90 50 A 40 40 0 0 1 10 50 L 20 50 A 30 30 0 0 0 80 50 Z"' \
        annulus.png

annulus

Other great example here, and here

update

To create a more programmatic approach, use any OOP scripting language. Below is quick example with Python & Wand, but Ruby & RMagick are also highly recommended.

#!/usr/bin/env python3

import math

from wand.color import Color
from wand.drawing import Drawing
from wand.image import Image

class Annulus(Image):
  def __init__(self, inner, outer, padding=5):
    self.Ri = inner
    self.Ro = outer
    side = (outer + padding) * 2
    self.midpoint = side/2
    super(Annulus, self).__init__(width=side,
                                  height=side,
                                  background=Color("transparent"))
    self.format = 'PNG'

  def __iadd__(self, segment):
    cos_start, cos_end = math.cos(segment.As), math.cos(segment.Ae)
    sin_start, sin_end = math.sin(segment.As), math.sin(segment.Ae)
    SiX, SiY = self.midpoint + self.Ri * cos_start, self.midpoint + self.Ri * sin_start
    SoX, SoY = self.midpoint + self.Ro * cos_start, self.midpoint + self.Ro * sin_start
    EiX, EiY = self.midpoint + self.Ri * cos_end,   self.midpoint +  self.Ri * sin_end
    EoX, EoY = self.midpoint + self.Ro * cos_end,   self.midpoint + self.Ro * sin_end
    with Drawing() as draw:
      for key, value in segment.draw_args.items():
        setattr(draw, key, value)
      draw.path_start()
      draw.path_move(to=(SiX, SiY))
      draw.path_elliptic_arc(to=(EiX, EiY),
                             radius=(self.Ri, self.Ri),
                             clockwise=True)
      draw.path_line(to=(EoX, EoY))
      draw.path_elliptic_arc(to=(SoX, SoY),
                             radius=(self.Ro, self.Ro),
                             clockwise=False)
      draw.path_close()
      draw.path_finish()
      draw(self)
    return self

class Segment(object):
  def __init__(self, start=0.0, end=0.0, **kwargs):
    self.As = start
    self.Ae = end
    self.draw_args = kwargs

if __name__ == '__main__':
  from wand.display import display
  ring  = Annulus(20, 40)
  ring += Segment(start=0,
                  end=math.pi/2,
                  fill_color=Color("yellow"))
  ring += Segment(start=math.pi/2,
                  end=math.pi,
                  fill_color=Color("pink"),
                  stroke_color=Color("magenta"),
                  stroke_width=1)
  ring += Segment(start=math.pi,
                  end=0,
                  fill_color=Color("lime"),
                  stroke_color=Color("orange"),
                  stroke_width=4)
  display(ring)

programmatic annulus

emcconville
  • 23,800
  • 4
  • 50
  • 66
0

I know little about gnuplot but think it probaby fits the bill here - my commands may be crude, but they seem pretty legible and effective. Someone cleverer than me may be able to improve them!

Anyway, here is the script I came up with:

set xrange [-1:1]
set yrange [-1:1]
set angles degrees
set size ratio -1
# r1 = annulus outer radius, r2 = annulus inner radius
r1=1.0
r2=0.8
unset border; unset tics; unset key; unset raxis
set terminal png size 1000,1000
set output 'output.png'
set style fill solid noborder
set object circle at first 0,0 front size r1 arc [0:60]    fillcolor rgb 'red'
set object circle at first 0,0 front size r1 arc [60:160]  fillcolor rgb 'green'
set object circle at first 0,0 front size r1 arc [160:360] fillcolor rgb 'blue'

# Splat a white circle on top to conceal central area
set object circle at first 0,0 front size r2 fillcolor rgb 'white'

plot -10 notitle

And here is the result:

enter image description here

So, if you save the above script as annulus.cmd you would run it and create the file output.png using the command

gnuplot annulus.cmd

Obviously the guts of the script are the 3 lines that start set object circle each of which creates a separate annulus segment in a different colour with a different set of start and end angles.

Noodling around and changing some things gives this:

set xrange [-1:1]
set yrange [-1:1]
set angles degrees
set size ratio -1
# r1 = annulus outer radius, r2 = annulus inner radius
r1=1.0
r2=0.4
unset border; unset tics; unset key; unset raxis
set terminal png size 1000,1000
set output 'output.png'
set style fill solid noborder
set object circle at first 0,0 front size r1 arc [0:60]    fillcolor rgb 'red'
set object circle at first 0,0 front size r1 arc [60:120]  fillcolor rgb 'green'
set object circle at first 0,0 front size r1 arc [120:180] fillcolor rgb 'blue'
set object circle at first 0,0 front size r1 arc [180:240] fillcolor rgb 'yellow'
set object circle at first 0,0 front size r1 arc [240:300] fillcolor rgb 'black'
set object circle at first 0,0 front size r1 arc [300:360] fillcolor rgb 'magenta'

# Splat a white circle on top to conceal central area
set object circle at first 0,0 front size r2 fillcolor rgb 'white'

plot -10 notitle

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
0

As I am better at thinking in straight lines than circles, I thought I would have another go at this, a totally different way...

First, draw our annulus out in a straight line like this:

convert -size 45x40 xc:red xc:lime -size 270x40 xc:blue +append line.png

enter image description here

I sneakily made the lengths of the line segments add up to 360, so that there is one pixel per degree - for my simple brain to cope with :-) So, there are 45 px (degrees) of red, 45 px (degrees) of lime and 270 pixels (degrees) of blue, and they are all appended together with +append to make the line. Note that the first -size 45x40 setting persists until later changed, so it applies to both the red and lime line segments before I change it ready to apply to the blue.

Now we bend that line around a circle, like this:

convert line.png -virtual-pixel White -distort arc 360 result.png

enter image description here

You can also do it all in one go when you get used to the concept, like this:

convert -size 60x40 xc:red xc:lime xc:blue xc:cyan xc:magenta xc:black +append -virtual-pixel White -distort arc 360 result.png

enter image description here

You can add grey borders to your annulus segments like this:

convert -size 600x400 xc:red xc:lime xc:blue xc:cyan xc:magenta xc:black -bordercolor "rgb(180,180,180)" -border 20 +append -virtual-pixel White -distort arc 360 result.png

enter image description here

If you want everything on a transparent background, change all the white above to none.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432