!  Clipping, scaling, zooming

integer  xoffset=0,yoffset=0
real  xscale=1,yscale=1
integer  vl = 0,vr = 688,vb = 0,vt = 512; !Viewport (physical coordinates)
integer  wl = 0,wr = 688,wb = 0,wt = 512; !Window   (virtual  coordinates)
real  screen aspect ratio = ((vt-vb)*4)/((vr-vl)*3)

routine  scale(integername  x,y); !Map to device coords
  x = intpt(x*xscale)+xoffset
  y = intpt(y*yscale)+yoffset
end 

routine  align window with viewport
! Make sure scaled middle of virtual window
! maps to middle of device window
integer  vx = (vl+vr)//2, vy = (vb+vt)//2
integer  wx = (wl+wr)//2, wy = (wb+wt)//2
  xoffset = 0; yoffset = 0; scale(wx,wy)
  xoffset = vx-wx; yoffset = vy-wy
end 

routine  aspect ratio
real  f
  f = |yscale/xscale/screen aspect ratio|
  if  f>1 then  yscale = yscale/f else  xscale = xscale*f
  align window with viewport
end 

routine  compute scalings
  xscale = (vr-vl)/(wr-wl)
  yscale = (vt-vb)/(wt-wb)
  aspect ratio
end 

routine  viewport(integer  x1,y1,x2,y2)
  vl = x1; vb = y1; vr = x2; vt = y2
  compute scalings
end 

routine  window(integer  x1,y1,x2,y2)
  wl = x1; wr = x2; wt = y2; wb = y1
  compute scalings
end 

routine  zoom(integer  tx,ty,real  f)
! TX, TY are the centre of the new window
integer  wx = intpt((wr-wl)/f)
integer  wy = intpt((wt-wb)/f)
  wl = tx-wx//2; wr = wl+wx
  wb = ty-wy//2; wt = wb+wy
  compute scalings
end 

integerfn  clipcode(integer  x,y)
integer  c=0
  c = 1 if  x<vl
  c = c+2 if  x>vr
  c = c+4 if  y<vb
  c = c+8 if  y>vt
  result =c
end 

routine  clipped line(integer  x1,y1,x2,y2)
integer  c1 = clipcode(x1,y1)
integer  c2 = clipcode(x2,y2)
integer  dx,dy

  routine  into range(integername  x,y)
    y = intpt((vl-x)*dy/dx)+y and  x = vl if  x<vl
    y = intpt((vr-x)*dy/dx)+y and  x = vr if  x>vr
    x = intpt((vb-y)*dx/dy)+x and  y = vb if  y<vb
    x = intpt((vt-y)*dx/dy)+x and  y = vt if  y>vt
  end 

  returnunless  c1&c2=0
  unless  c1!c2=0 start 
    dx = x2-x1; dy = y2-y1
    into range(x1,y1)
    into range(x2,y2)
  finish 
  line(x1,y1,x2,y2)
end 

routine  scaled line(integer  x1,y1,x2,y2)
  scale(x1,y1); scale(x2,y2)
  clipped line(x1,y1,x2,y2)
end 

routine  clipped fill(integer  x1,y1,x2,y2)
integer  c1 = clipcode(x1,y1)
integer  c2 = clipcode(x2,y2)

  routine  sw(integername  a,b)
  integer  c
    c = a; a = b; b = c
  end 

  returnunless  c1&c2=0
  sw(x1,x2) if  x1>x2
  sw(y1,y2) if  y1>y2
  x1 = vl if  x1<vl
  y1 = vb if  y1<vb
  x2 = vr if  x2>vr
  y2 = vt if  y2>vt
  fill(x1,y1,x2,y2)
end 

routine  scaled fill(integer  x1,y1,x2,y2)
  scale(x1,y1); scale(x2,y2)
  clipped fill(x1,y1,x2,y2)
end 

endoffile