fortranbinaryfilesstl-format

Writing To a Binary File


I am attempting to write an STL Binary file in Fortran 90. The file has the following format

HEADER: An 80 byte ASCII header - TITLE

A 4 byte unsigned long integer, NO. OF FACETS

FORMAT FOR EACH FACET:

Normal vector, 3 floating values of 4 bytes each;

Vertex 1 XYZ coordinates, 3 floating values of 4 bytes each;

Vertex 2 XYZ coordinates, 3 floating values of 4 bytes each;

Vertex 3 XYZ coordinates, 3 floating values of 4 bytes each;

An unsigned integer, of 2 bytes, that should be zero;

I am attempting to create an unformatted file for writing the relevant information but am having trouble defining the correct record length. Assuming that I have N facets, I am using the following commands to open and write information

open(unit = 1, status = 'replace', iostat = ioerror, format = 'unformatted', access = 'direct', recl = 84 + N * 50, file = 'c:\temp\test.stl')

Can I issue the first write statement to write out the header information followed by the second write statement (within a do loop) to write out the facet information?

If so, what would be the record number I need to have for each of the write statements since I have the header and the facet information with different record lengths.

write(1,rec=?), *header information*
do,i=1,N,1
   write(1,rec=?), *facet information*
enddo

Solution

  • That was interesting. I have hacked together a small program that creates a very simple pyramid using the STREAM access. It seems to work:

    program write_stl
        use ISO_FORTRAN_ENV
        implicit none
        integer, parameter :: u = 400
        character(len=*), parameter :: fname = 'pyramid.stl'
        integer :: ios
        integer(kind=int32) :: num_facets
        character(len=80) :: title
        real(kind=real32), dimension(3) :: top_vertex, front_vertex, left_vertex, right_vertex
    
        top_vertex = (/0.0, 0.0, 2.0/)
        front_vertex = (/0.0, -1.0, 0.0/)
        left_vertex = (/-1.0, 1.0, 0.0/)
        right_vertex = (/1.0, 1.0, 0.0/)
    
        open(unit=u, file=fname, access='stream', status='replace', &
            action='write', iostat=ios)
        call check(ios, 'open')
    
        title = "Testpyramid"
        write(u, iostat=ios) title
        call check(ios, 'write title')
        num_facets = 4
        write(u, iostat=ios) num_facets
        call check(ios, 'write number of facets')
        ! bottom facet
        call write_facet(u, front_vertex, left_vertex, right_vertex)
        call write_facet(u, top_vertex, right_vertex, left_vertex)
        call write_facet(u, top_vertex, left_vertex, front_vertex)
        call write_facet(u, top_vertex, front_vertex, right_vertex)
    
        close(u, iostat=ios)
        call check(ios, 'close')
    
    contains
    
        subroutine check(ios, operation)
            implicit none
            integer, intent(in) :: ios
            character(len=*), intent(in) :: operation
            if (ios == 0) return
            write(*, '(A, I0, 2A)') "Encountered error ", ios, " while performing ", operation
            stop 1
        end subroutine check
    
        subroutine write_facet(u, vertex1, vertex2, vertex3)
            implicit none
            integer, intent(in) :: u
            real(kind=real32), dimension(3), intent(in) :: vertex1, vertex2, vertex3
            real(kind=real32), dimension(3) :: normal
            integer(kind=int16), parameter :: zero = 0
    
            normal = calc_normal(vertex1, vertex2, vertex3)
            write(u, iostat=ios) normal
            call check(ios, 'write normal')
            write(u, iostat=ios) vertex1
            call check(ios, 'write vertex')
            write(u, iostat=ios) vertex2
            call check(ios, 'write vertex')
            write(u, iostat=ios) vertex3
            call check(ios, 'write vertex')
            write(u, iostat=ios) zero
            call check(ios, 'write zero')
        end subroutine write_facet
    
        function calc_normal(vec1, vec2, vec3)
            implicit none
            real(kind=real32), dimension(3), intent(in) :: vec1, vec2, vec3
            real(kind=real32), dimension(3) :: calc_normal
            real(kind=real32), dimension(3) :: d1, d2
            d1 = vec2 - vec1
            d2 = vec3 - vec1
            calc_normal(1) = d1(2) * d2(3) - d1(3) * d2(2)
            calc_normal(2) = d1(3) * d2(1) - d1(1) * d2(3)
            calc_normal(3) = d1(1) * d2(2) - d1(2) * d2(1)
            calc_normal = calc_normal / norm(calc_normal)
        end function calc_normal
    
        function norm(vec)
            implicit none
            real(kind=real32), dimension(3), intent(in) :: vec
            real(kind=real32) :: norm
    
            norm = sqrt(vec(1)**2 + vec(2)**2 + vec(3)**2)
        end function norm
    
    end program write_stl