make_scratches Subroutine

private subroutine make_scratches()

Note

This subroutine initializes a real matrix tab of dimensions nx by ny with ones

Arguments

None

Called by

proc~~make_scratches~~CalledByGraph proc~make_scratches make_scratches proc~read_job read_job proc~read_job->proc~make_scratches proc~prg_surf prg_surf proc~prg_surf->proc~read_job program~main main program~main->proc~prg_surf

Source Code

   subroutine make_scratches()
   !================================================================================================
   !< @note This subroutine initializes a real matrix `tab` of dimensions `nx` by `ny` with ones
   !        and adds `sn` linear scratches with value 0. Each scratch has a random length up to `sl`,
   !        a constant width `sw`, and a random orientation. Scratches can touch the matrix boundaries.
   !
   !  @endnote
   !------------------------------------------------------------------------------------------------
   implicit none

      integer(kind=I4) :: nx   ! Number of rows
      integer(kind=I4) :: ny   ! Number of columns
      integer(kind=I4) :: sn   ! Number of scratches
      integer(kind=I4) :: sw   ! Scratch width
      integer(kind=I4) :: sl   ! Maximum scratch length

      integer(kind=I4) :: i, j, n
      real(kind=R8)    :: theta, length, depth, x0, y0, x1, y1
      real(kind=R8)    :: rand, r1, r2

      nx = PARAM%width
      ny = PARAM%height

      read(JOB,*)  sn, sw, sl ; LINE_READ = LINE_READ + 1 ; write(SPY,*) "line: ", LINE_READ, "sn, sw, sl  ", sn, sw, sl

      ! Initialize matrix with ones
      PARAM%surf(1:nx, 1:ny) = 1.0_R8

      ! Seed random number generator
      call random_seed()

      ! Generate each scratch
      do n = 1, sn

         ! Random starting point
         call random_number(rand)
         x0 = rand * nx
         call random_number(rand)
         y0 = rand * ny

         ! Random depth
         ! Génération de deux variables gamma
         call gamma_random(shape = 3._R8, output = r1)
         call gamma_random(shape = 1._R8, output = r2)
         ! Transformation pour obtenir la loi bêta
         depth = r1 / (r1 + r2)

         ! Random orientation (angle in radians)
         call random_number(rand)
         theta = rand * 2.0 * PI_R8

         ! Random length up to sl
         call random_number(rand)
         length = ( 2 * (rand - 0.5_R8) / 10 + 0.9_R8 ) * sl

         ! Calculate end point of the scratch
         x1 = x0 + length * cos(theta)
         y1 = y0 + length * sin(theta)

         ! Draw the scratch with width sw
         do i = max(1, floor(min(x0, x1)) - sw), min(nx, ceiling(max(x0, x1)) + sw)

            do j = max(1, floor(min(y0, y1)) - sw), min(ny, ceiling(max(y0, y1)) + sw)

               ! Check if point (i,j) is within sw/2 distance from the line segment
               if ( point_to_line_distance(real(i, kind=R8), real(j, kind=R8), x0, y0, x1, y1) <= sw/2.0 ) then

                  PARAM%surf(i, j) = depth

               endif

            enddo

         enddo

      enddo

   contains

      subroutine gamma_random(shape, output)
      implicit none
      real(kind=R8), intent( in) :: shape
      real(kind=R8), intent(out) :: output

         real(kind=R8) :: b, c
         real(kind=R8) :: u, v, w, y

         ! algorithme de marsaglia pour générer une variable gamma
         b = shape - 1.0/3.0
         c = 1.0 / sqrt(9.0 * b)

         do

            call random_number(u)
            call random_number(v)

            v = 1.0 + c * log(v) / sqrt(u)

            if (v <= 0.0) cycle

            w = v * v * v

            call random_number(y)

            if (y < 1.0 - 0.0331 * (log(v) ** 4)) exit

            if (log(y) < 0.5 * log(v) * log(v) + b * (1.0 - v + log(v))) exit

         enddo

         output = b * w

      return
      endsubroutine gamma_random
      !-----------------------------------------
      real(kind=R8) function point_to_line_distance(px, py, x0, y0, x1, y1)
         !! Calculate the shortest distance from a point to a line segment.
      implicit none
      real(kind=R8), intent(in) :: px  !! x-coordinate of the point
      real(kind=R8), intent(in) :: py  !! y-coordinate of the point
      real(kind=R8), intent(in) :: x0  !! x-coordinate of the line segment start
      real(kind=R8), intent(in) :: y0  !! y-coordinate of the line segment start
      real(kind=R8), intent(in) :: x1  !! x-coordinate of the line segment end
      real(kind=R8), intent(in) :: y1  !! y-coordinate of the line segment end

         real(kind=R8) :: dx, dy, t, proj_x, proj_y

         dx = x1 - x0
         dy = y1 - y0

         ! Handle case where start and end points are the same
         if ( (dx**2 + dy**2) < 1.e-12 ) then

            point_to_line_distance = sqrt((px - x0)**2 + (py - y0)**2)

            return

         endif

         ! Project point onto the line
         t = max(0.0_R8, min(1.0_R8, ((px - x0)*dx + (py - y0)*dy)/(dx*dx + dy*dy)))

         proj_x = x0 + t * dx
         proj_y = y0 + t * dy

         ! Calculate distance
         point_to_line_distance = sqrt( (px - proj_x)**2 + (py - proj_y)**2 )

      return
      endfunction point_to_line_distance
      !-----------------------------------------

   endsubroutine make_scratches