Files
mercury/benchmarks/progs/ray/off.m
Paul Bone ea06fd8cde Add the benchmarks directory into the main Mercury repository.
This was a seperate repository in CVS and so it missed the conversion.

benchmarks/
    As above.
2013-01-04 12:13:53 +11:00

412 lines
13 KiB
Mathematica

% vim: ts=4 sw=4 expandtab
:- module off.
% read in an off object and put it in the standard form: i.e. scale it so it
% just fits in a 1x1x1 cube and centre it at the origin.
:- interface.
:- import_module io, int, list, vec3, gfx.
:- type off_object --->
obj(
list(vec3), % list of vertices
list(off_face) % list of faces
).
:- type off_face --->
face(
int, % number of vertices
list(int), % list of vertex indices
colour % intrinsic colour of face
).
% read_off(Filename, Object)
% read Object from Filename and ``standardise'' it
:- pred read_off(string, off_object, io__state, io__state).
:- mode read_off(in, out, di, uo) is det.
:- implementation.
:- import_module char, require, string, split_line, float, debug.
read_off(Filename, Object) -->
(
io__open_input(Filename, Result),
(
{ Result = error(ErrorCode) },
{ io__error_message(ErrorCode, ErrorString) },
{ error(ErrorString) }
;
{ Result = ok(Stream) },
io__set_input_stream(Stream, OldStream), % set input stream to the off file
read_header(NumVertices, NumFaces),
read_vertices(NumVertices, Vertices0),
read_faces(NumFaces, Faces),
{ standardise(Vertices0, Vertices) },
{ Object = obj(Vertices, Faces) },
io__set_input_stream(OldStream, _), % set input stream back to the scene file
io__close_input(Stream)
)
).
% read_header(NumVertices, NumFaces)
% read the off file header return the number of vertices and faces
:- pred read_header(int, int, io__state, io__state).
:- mode read_header(out, out, di, uo) is det.
read_header(NumVertices, NumFaces) -->
(
get_next_word(Word0),
(
% detect optional "OFF" keyword at start of file
{ Word0 = "OFF" }
->
get_next_word(Word1)
;
{ Word1 = Word0 }
),
(
{ string__to_int(Word1, X1) }
->
{ NumVertices = X1 }
;
io__input_stream_name(Filename),
{ string__append(Filename, ": Error reading OFF header", ErrorString) },
{ error(ErrorString) }
),
get_next_word(Word2),
(
{ string__to_int(Word2, X2) }
->
{ NumFaces = X2 }
;
io__input_stream_name(Filename),
{ string__append(Filename, ": Error reading OFF file", ErrorString1) },
{ error(ErrorString1) }
),
get_next_word(_) % read in and ignore number of edges
).
% read in vertices
:- pred read_vertices(int, list(vec3), io__state, io__state).
:- mode read_vertices(in, out, di, uo).
read_vertices(N, L) -->
(
{ N =< 0 }
->
{ L = [] }
;
get_next_float(X),
get_next_float(Y),
get_next_float(Z),
{ V = vec(X, Y, Z) },
read_vertices(N - 1, L1),
{ L = [V | L1] }
).
% read in next space-delimited word and convert to a float
:- pred get_next_float(float, io__state, io__state).
:- mode get_next_float(out, di, uo) is det.
get_next_float(F) -->
(
get_next_word(W),
(
{ string__to_float(W, F1) }
->
{ F = F1 }
;
io__input_stream_name(FileName),
{ string__append(FileName, ": Error reading OFF vertex list", ErrorString) },
{ error(ErrorString) }
)
).
% get next space-delimited word from the OFF file, strip comments and return a
% string. Call error/1 if string can't be read in.
:- pred get_next_word(string, io__state, io__state).
:- mode get_next_word(out, di, uo) is det.
get_next_word(Word) -->
get_next_word0(CharList),
{ string__from_char_list(CharList, Word) }.
:- pred get_next_word0(list(char), io__state, io__state).
:- mode get_next_word0(out, di, uo) is det.
get_next_word0(L) -->
(
io__read_word(Result),
(
{ Result = eof },
io__input_stream_name(Filename),
{ string__append(Filename, ": OFF file too short", ErrorString) },
{ error(ErrorString) }
;
{ Result = error(ErrorCode) },
{ io__error_message(ErrorCode, ErrorString1) },
{ error(ErrorString1) }
;
{ Result = ok(L0) },
strip_comments_from_word(L0, L1),
(
{ L1 = [] }
->
get_next_word0(L)
;
{ L = L1 }
)
)
).
:- pred strip_comments_from_word(list(char), list(char), io__state, io__state).
:- mode strip_comments_from_word(in, out, di, uo) is det.
strip_comments_from_word(Lin, Lout) -->
(
{ Lin = [] },
{ Lout = [] }
;
{ Lin = [C | L0] },
(
{ C = '#'}
->
{ Lout = [] },
io__read_line(_) % discard rest of line
;
strip_comments_from_word(L0, L1),
{ Lout = [C | L1] }
)
).
% read_faces(NumFaces, Faces)
% read in the faces from the OFF file
% each line of the file contains 1 face
:- pred read_faces(int, list(off_face), io__state, io__state).
:- mode read_faces(in, out, di, uo) is det.
read_faces(N, Fs) -->
(
{ N =< 0}
->
{ Fs = [] }
;
io__read_line(Result),
(
{ Result = eof },
io__input_stream_name(Filename),
{ string__append(Filename, ": OFF file too short", ErrorString) },
{ error(ErrorString) }
;
{ Result = error(ErrorCode) },
{ io__error_message(ErrorCode, ErrorString1) },
{ error(ErrorString1) }
;
{ Result = ok(L0) },
io__input_stream_name(Filename),
{ strip_comments_from_line(L0, L1) },
{ split_line(L1, Words0) },
(
% check for a blank line
{ Words0 = [] },
read_faces(N, Fs)
;
{ Words0 = [W1 | Words1] },
%{ dump("off.m:read_faces W1 **%s**", [s(W1)]) }, %% dmo
(
{ W1 = ""}
->
read_faces(N, Fs)
;
(
{ string__to_int(W1, N1) }
->
{ NumVerts = N1 },
%{ dump("off.m:read_faces about to call get_vertex_list for %d vertices\n", [i(NumVerts)]) }, %dmo
{ get_vertex_list(NumVerts, Filename, VertList, Words1, Words2) },
{ get_colour(Words2, Colour) },
{ P = face(NumVerts, VertList, Colour) },
read_faces(N - 1, Fs1),
{ Fs = [P | Fs1] }
;
{ string__append(Filename, ": Error reading OFF faces", ErrorString2) },
{ error(ErrorString2) }
)
)
)
)
).
% strip comments from a line of the file (list of chars)
:- pred strip_comments_from_line(list(char), list(char)).
:- mode strip_comments_from_line(in, out) is det.
strip_comments_from_line(Lin, Lout) :-
(
Lin = [],
Lout = []
;
Lin = [C | L1],
(
C = '#'
->
Lout = []
;
strip_comments_from_line(L1, L2),
Lout = [C | L2]
)
).
% get the list of vertex indices for a face
% filename is passed in for error reporting
:- pred get_vertex_list(int, string, list(int), list(string), list(string)).
:- mode get_vertex_list(in, in, out, in, out) is det.
get_vertex_list(N, Filename, Vs, WordsIn, WordsOut) :-
(
N =< 0
->
Vs = [],
WordsOut = WordsIn
;
(
WordsIn = [],
string__append(Filename, ": Error reading OFF faces", ErrorString),
error(ErrorString)
;
WordsIn = [W | Ws1],
(
string__to_int(W, I)
->
%dump("off.m:get_vertex_list: found vertex %d\n", [i(I)]), %dmo
get_vertex_list(N - 1, Filename, Vs1, Ws1, WordsOut),
Vs = [I | Vs1]
;
string__append(Filename, ": Error reading OFF faces", ErrorString1),
error(ErrorString1)
)
)
).
% get colour for face
:- pred get_colour(list(string), colour).
:- mode get_colour(in, out) is det.
get_colour(Words, Colour) :-
(
Words = [W1, W2, W3 | _]
->
(
string__to_int(W1, I1), 0 =< I1, I1 =< 255,
string__to_int(W2, I2), 0 =< I2, I2 =< 255,
string__to_int(W3, I3), 0 =< I3, I3 =< 255
->
F1a = float(I1),
F2a = float(I2),
F3a = float(I3),
Colour = colour(F1a/255.0, F2a/255.0, F3a/255.0)
;
string__to_float(W1, F1), 0.0 =< F1, F1 =< 1.0,
string__to_float(W2, F2), 0.0 =< F2, F2 =< 1.0,
string__to_float(W3, F3), 0.0 =< F3, F3 =< 1.0
->
Colour = colour(F1, F2, F3)
;
% if can't convert properly, use default colour
Colour = default_colour
)
;
Words = [W1 | _]
->
% if a single number is given, try to use it as a greyscale value
(
string__to_int(W1, I1), 0 =< I1, I1 =< 255
->
F1a = float(I1),
F1b = F1a/255.0,
Colour = colour(F1b, F1b, F1b)
;
string__to_float(W1, F1), 0.0 =< F1, F1 =< 1.0
->
Colour = colour(F1, F1, F1)
;
Colour = default_colour
)
;
Colour = default_colour
).
% transform the object into standard form. IE transform to fit into a 1x1 cube
% centred at the origin.
:- pred standardise(list(vec3), list(vec3)).
:- mode standardise(in, out) is det.
standardise(VLin, VLout) :-
(
get_min_max(VLin, MinX, MaxX, MinY, MaxY, MinZ, MaxZ),
% dump("get_min_max results: %f %f %f %f %f %f\n", [f(MinX), f(MaxX), f(MinY), f(MaxY), f(MinZ), f(MaxZ)]), % debug - dmo
Max1 = float__max(MaxX - MinX, MaxY - MinY),
MaxExtent = float__max(Max1, MaxZ - MinZ),
veclist_translate(VLin, vec(-(MinX+MaxX)/2.0, -(MinY+MaxY)/2.0, -(MinZ+MaxZ)/2.0), VL1),
( MaxExtent = 0.0 -> error("Max Extent is 0!!") ; true ),
veclist_scale(VL1, 1.0/MaxExtent, VLout)
).
% find min and max extents in each direction
:- pred get_min_max(list(vec3), float, float, float, float, float, float).
:- mode get_min_max(in, out, out, out, out, out, out) is det.
get_min_max(VLin, MinX, MaxX, MinY, MaxY, MinZ, MaxZ) :-
(
VLin = [],
MinX = float__max,
MinY = float__max,
MinZ = float__max,
MaxX = float__min,
MaxY = float__min,
MaxZ = float__min
;
VLin = [vec(X, Y, Z) | VL1],
%dump("get_min_max: current vector %f %f %f\n", [f(X), f(Y), f(Z)]), % debug - dmo
get_min_max(VL1, MinX1, MaxX1, MinY1, MaxY1, MinZ1, MaxZ1),
MinX = float__min(X, MinX1),
MinY = float__min(Y, MinY1),
MinZ = float__min(Z, MinZ1),
MaxX = float__max(X, MaxX1),
MaxY = float__max(Y, MaxY1),
MaxZ = float__max(Z, MaxZ1)
).
% translate all vectors in list
:- pred veclist_translate(list(vec3), vec3, list(vec3)).
:- mode veclist_translate(in, in, out) is det.
veclist_translate(VLin, VT, VLout) :-
(
VLin = [],
VLout = []
;
VLin = [V1 | VL1],
V2 = V1 + VT,
veclist_translate(VL1, VT, VL2),
VLout = [V2 | VL2]
).
% scale all vectors in list
:- pred veclist_scale(list(vec3), float, list(vec3)).
:- mode veclist_scale(in, in, out) is det.
veclist_scale(VLin, Scale, VLout) :-
(
VLin = [],
VLout = []
;
VLin = [V1 | VL1],
V2 = scale(Scale, V1),
veclist_scale(VL1, Scale, VL2),
VLout = [V2 | VL2]
).