function figure2xhtml(varargin)
% This function FIGURE2XHTML converts the 3D objects of a Matlab figure
% to an in XHTML embedded X3D file. In a modern browser, you can now view
% your figure interactively because of the great X3DOM Library
% (Instant 3D the HTML way! http://www.x3dom.org/).
%
% Currently the function supports: Axes, Patch, Line, Surface, Text, Image
% and Light objects.
%
% Browsers supported:
% - Google Chrome 9.x and above
% - Firefox 4.x and above
% - Webkit nightly builds
% - InstantReality-plugin or Flash11-plugin needed for Internet explorer
%
%
%  figure2xhtml(filename,handle,options)
%
%   Note : All input arguments are optional, and sorting doesn't matter
%          thus figure2xhtml(); is valid but also
%          figurexhtml(options,handle);
%
% inputs,
%    filename : Name of the XHTML file (also an X3D file is created).
%           When empty or not known a File-Dialog is shown
%    handle : Figure handle or axis handle
%           When empty or not known the current axis (GCA) is used
%    options : A struct with options
%      options.output : Produce output files 'x3d', 'xhtml' or 'both' (default)
%      options.width : Width of X3D render object in pixels default 500 
%      options.height : Height of X3D render object in pixels default 500 
%      options.headlight : Enable Camera head light, boolean true/false
%                           (default true)
%      options.embedimages : Instead of using separate .png files embed 
%                            the images in xhtml (Not supported bij IE9)
%                           (default false)
%      options.title : Title of xhtml page, default 'Matlab X3D'
%      options.interactive : Make mesh/surface objects clickable in xhtml,
%                          boolean true/false (default false)
%                           
%
% Example, Click-able patch, with face color
%  load('functions\exampledata');
%  figure, hold on; axis equal;
%  patch(FV,'facecolor',[1 0 0],'facealpha',0.5);
%  FV.vertices(:,1)=FV.vertices(:,1)+80;
%  patch(FV,'FaceColor','interp','FaceVertexCData',rand(size(FV.vertices)),'edgecolor','none');
%  FV.vertices(:,1)=FV.vertices(:,1)+80;
%  patch(FV,'FaceColor','flat','FaceVertexCData',rand(size(FV.faces)),'edgecolor','none');
%  figure2xhtml('test/example1',struct('interactive',true))
%
%Example, Lights and surface
%   logo
%   if(exist('l1','var'))
%     set(l1,'Position',[40 100 20]*2);
%     set(l2,'Position',[.5 -1 .4]*30);
%   end
%   figure2xhtml('test/example2.xhtml')
%
%Example, Surf with colors
%   h=figure, hold on; sphere;
%   figure2xhtml('test/example3',h)
%
%Example, Mesh
%   figure, axis([-3 3 -3 3 -10 5])
%   [X,Y] = meshgrid(-3:.125:3);
%   Z = peaks(X,Y);
%   mesh(X,Y,Z);
%   figure2xhtml('test/example4')
%
%Example, Surf
%   figure,
%   [X,Y] = meshgrid(-3:.125:3);
%   Z = peaks(X,Y);
%   surf(X,Y,Z);
%   figure2xhtml;
%
%Example, Lines
%   figure, hold on;
%   plot3([1 10 30]*4,[1 20 30]*4,[1 10 30]*4,'r:');
%   plot3([1 10 30]'*4,[1 20 30]'*4,[10 30 1]'*4,'g*');
%   plot3([1 5 20]'*4,[1 5 20]'*4,[3 20 1]'*4,'-g*','MarkerEdgeColor',[1 0 0]);
%   figure2xhtml('test/example5')
%
%Example, Points
%   L = 40*membrane(1,25);
%   [X,Y]=ndgrid(1:size(L,1),1:size(L,2));
%   figure, plot3(X(:),Y(:),L(:),'r.');
%   figure2xhtml('test/example6')
%
%Example, Texture on Surface
%   figure, hold on;
%   load('functions\exampledata');
%   surface(peaks,flipud(X),'FaceColor','texturemap','EdgeColor','none', 'CDataMapping','direct')
%   colormap(map)
%   view(-35,45)
%   figure2xhtml('test/example7')
%
%Example, Axis
%   h=figure, hold on; sphere;
%   addpath([cd '/functions'])
%   axis2lines
%   figure2xhtml('test/example8',h)
%
% Function is written by D.Kroon University of Twente (July 2011)

% Check input arguments
haxis=[]; filename=[]; options=[];
if(nargin>3)
   error('figure2xhtml:inputs','too much input variables');
end
for i=1:nargin
    val=varargin{i};
    if(ischar(val)),
        filename=val;
    elseif(isnumeric(val)),
        haxis=val;
    elseif(isstruct(val)), 
        options=val;
    else
        error('figure2xhtml:inputs','unknown input');
    end
end

% Process inputs
defaultoptions=struct('output','both','height',500,'width',500,'headlight',true,'title','Matlab X3D','interactive',false,'embedimages',false);
if(isempty(options)), options=defaultoptions;
else
    tags = fieldnames(defaultoptions);
    for i=1:length(tags), if(~isfield(options,tags{i})), options.(tags{i})=defaultoptions.(tags{i}); end, end
    if(length(tags)~=length(fieldnames(options))),
        warning('register_images:unknownoption','unknown options found');
    end
end

% Get axis handle of figure-handle or current handle
haxis=axis_handle(haxis);

if(isempty(filename))
    [filename, folder] = uiputfile({'*.xhtml','XHTML file (*.xhtml)';'*.x3d','X3D (*.x3d)'; '*.*', 'All Files (*.*)'}, 'Select an output filename');
    filename=[folder filename];
end

% Extension  of filename
[folder,filename,ext]=fileparts(filename);
if(~isempty(folder)), folder=[folder '/']; end

% Causes all graphics objects to be ready
drawnow('expose'); pause(0.1);

% add all needed function paths
add_function_paths;


% Create the xhtml file
if(strcmpi(options.output,'xhtml')||strcmpi(options.output,'both'))
    [data,loc_body]=XHTMLheader(options);
    data.tags.numobjects=0;
    data.tags.xhtml=true;
    data.tags.folder=folder;
    data.tags.filename=filename;
    data.tags.options=options;
    [data,loc_scene]=X3Dheader(data,loc_body,haxis);
    data=figurex3d(haxis,data,loc_scene);
    if(options.interactive)
        data=XMLaddNode('script',data,loc_body+1);
        data=XMLaddProperty('type','text/javascript',data);
        data=XMLaddString(interactive_js(data.tags.numobjects),data);
    end
    writexhtmlfile(folder,filename,data);
end

% Create the x3d file
if(strcmpi(options.output,'x3d')||strcmpi(options.output,'both'))
    data=struct;
    data.tags.numobjects=0;
    data.tags.xhtml=false;
    data.tags.folder=folder;
    data.tags.filename=filename;
    data.tags.options=options;
    [data,loc_scene]=X3Dheader(data,[],haxis);
    data=figurex3d(haxis,data,loc_scene);
    writex3dfile(folder,filename,data);
end

function data=figurex3d(handle,data,loc_scene)
% Get all Childeren of the current axis, and create an X3D xml structure
% of these 3D objects
Ch=get(handle,'Children');
for i=1:length(Ch)
    Obj=get(Ch(i));
    type=Obj.Type;
    switch(type)
        case 'axes'
        case 'patch'
            data=addmesh(data,loc_scene,Obj);
        case 'line'
            data=addline(data,loc_scene,Obj);
        case 'surface'
            [F,V,Cface,Cedge,E,T]=surf2FV(Obj);
            Obj.Faces=F;
            Obj.E=E;
            Obj.Vertices=V;
            Obj.FaceVertexCData=Cface;
            Obj.EdgeVertexCData=Cedge;
            Obj.TextureVertices=T;
            data=addmesh(data,loc_scene,Obj);
        case 'image'
            C=Obj.CData;
            xd=[Obj.XData(1) Obj.XData(end)];
            yd=[Obj.YData(1) Obj.YData(end)];
            xstep=(xd(end)-xd(1))/size(C,2);
            ystep=(yd(end)-yd(1))/size(C,1);
            xd=xd+[-xstep/2 xstep/2];
            yd=yd+[-ystep/2 ystep/2];
            Obj.XData = linspace(xd(1),xd(2),size(C,2)+1);
            Obj.YData = linspace(yd(1),yd(2),size(C,1)+1)';
            Obj.ZData = zeros([size(C,1) size(C,2)]+1);
            Obj.EdgeColor = 'none';
            Obj.FaceColor = 'texturemap';
            Obj.FaceAlpha= 1;
            [F,V,Cface,Cedge,E,T]=surf2FV(Obj);
            Obj.Faces=F;
            Obj.E=E;
            Obj.Vertices=V;
            Obj.FaceVertexCData=Cface;
            Obj.EdgeVertexCData=Cedge;
            Obj.TextureVertices=T;
            data=addmesh(data,loc_scene,Obj);
        case 'light'
            data=addlight(data,loc_scene,Obj);
        case 'text'
            data=addtext(data,loc_scene,Obj);
        otherwise
            warning('figure2xhtml:figurobj',[type ' not supported']);
    end
end

function add_function_paths()
% add all needed function paths
try
    functionname='figure2xhtml.m';
    functiondir=which(functionname);
    functiondir=functiondir(1:end-length(functionname));
    addpath([functiondir '/functions'])
    addpath([functiondir '/functions_xml'])
catch me
    disp(me.message);
end

function hout=axis_handle(hin)
% Get axis handle of figure-handle or current handle
if(isempty(hin)),
    hout=gca;
else
    type=get(hin,'Type');
    if(~strcmpi(type,'axes'))
        Ch=get(hin,'Children');
        for i=1:length(Ch)
            type=get(Ch(i),'Type');
            if(strcmpi(type,'axes')), hout=Ch(i); break; end
        end
    else
        hout=hin;
    end
end


