Skip to content

Drawing

kkrmno edited this page May 15, 2020 · 7 revisions

To draw lines or disk shapes on top of an image, use draw_line and draw_disk.

Drawing Lines

The method draw_line takes 5 or 6 arguments (the width argument is optional) specifying the start and end points of the line as well as its color and, optionally, its width.

The following line draws a red line from (x0, y0) to (x1, y1) of width 1. The coordinates as well as the width can be floats. The color argument must have the same number of channels as the image on which the line is being drawn.

img_with_line = img.draw_line(x0, y0, x1, y1, [255, 0, 0], 1)

Below we show a more involved example where draw_line is used to create the following animation:

Although not visible on a white background, the image fades out towards the edges by decreasing alpha after a certain distance from the center of the image. Thus the image can be placed on top of any background without creating a jarring white square around it.

require 'imgrb'

#Two 'planets' orbiting around a common center at different distances and speeds
radius0 = 60
radius1 = 100
angle_speed0 = Math::PI/(7*3)
angle_speed1 = Math::PI/(13*3)
planet0_angle = 0
planet1_angle = 0

width = (radius1+35)*2
height = width
center_x = width/2.0
center_y = height/2.0

anim = Imgrb::Image.new(width, height, [255, 255])

#Create a disk shaped white background that fades out (alpha -> 0 as we approach
#the edge of the image)
fade_out_dist = 32
bg_limit_border = 5
coeff = -255.0/(fade_out_dist - bg_limit_border)
const = -coeff*(radius1 + fade_out_dist)

anim.height.times do |y|
  anim.width.times do |x|
    center = width / 2
    dx = x - center
    dy = y - center
    dist = Math.sqrt(dx**2 + dy**2)
    alpha = coeff*dist + const
    alpha = [0, alpha].max
    alpha = [alpha, 255].min
    alpha = alpha
    anim[y, x] = [255, alpha.round]
  end
end


##
#Update 'planet' positions for a number of time steps and draw straight lines
#between old and new locations for each planet as well as a line between the
#two planets.
planet0_pos = [Math.cos(planet0_angle)*radius0,
               Math.sin(planet0_angle)*radius0]
planet1_pos = [Math.cos(planet1_angle)*radius1,
               Math.sin(planet1_angle)*radius1]
546.times do |i|
  frame = Imgrb::Image.new(width, height, [0, 0])

  planet0_pos_old = planet0_pos
  planet1_pos_old = planet1_pos

  planet0_pos = [Math.cos(planet0_angle)*radius0,
                 Math.sin(planet0_angle)*radius0]
  planet1_pos = [Math.cos(planet1_angle)*radius1,
                 Math.sin(planet1_angle)*radius1]

  frame = frame.draw_line(planet0_pos[0]+width/2, planet0_pos[1]+height/2,
                          planet1_pos[0]+width/2, planet1_pos[1]+height/2, [0,55], 1)

  frame = frame.draw_line(planet0_pos_old[0]+center_x, planet0_pos_old[1]+center_y,
                          planet0_pos[0]+center_x, planet0_pos[1]+center_y, [0, 55], 1)

  frame = frame.draw_line(planet1_pos_old[0]+center_x, planet1_pos_old[1]+center_y,
                          planet1_pos[0]+center_x, planet1_pos[1]+center_y, [0, 55], 1)

  #Safer to do anim.push_frame(frame.round, ...), since frame contains floats,
  #but skipping is faster, and the floats should be truncated without issue.
  anim.push_frame(frame, 0, 0, 1, 60, :none, :over)

  planet0_angle += angle_speed0
  planet1_angle += angle_speed1
end

#Pause the animation for 5 seconds
pause_frame = Imgrb::Image.new(1,1,[0,0])
anim.push_frame(pause_frame,0,0,5,1,:none,:over)

#Create a white translucent disk that is overlaid on top of the image several
#times to fade to white over time.
fade_frame = Imgrb::Image.new(width, height, [255, 0])
fade_frame.height.times do |y|
  fade_frame.width.times do |x|
    center = width / 2
    dx = x - center
    dy = y - center
    dist = Math.sqrt(dx**2 + dy**2)
    if dist <= radius1+5
      fade_frame[y, x] = [255, 30]
    end
  end
end

30.times{anim.push_frame(fade_frame,0,0,1,30,:none,:over)}


anim.add_comment("Line animation")
anim.save("anim_orbit.png")

Drawing Disks

The method draw_disk takes 4 arguments specifying the origin of the disk as well as its radius and color. The following line draws a purple disk centered at (x0, y0) with a radius of 5.5. The coordinates as well as the radius can be floats. The color argument must have the same number of channels as the image on which the disk is being drawn.

img_with_disk = img.draw_disk(x0, y0, 5.5, [155, 0, 155])