BodyBuilder

This is the top-level class.

Contents

classdef BodyBuilder < handle
    % This defines the BodyBuilder class.

    %   URL : $URL: $
    %   Log : $Id: BodyBuilder.html,v 1.1 2008/07/23 12:51:18 jberg Exp $
    %   Copyright (c) 2008 The MathWorks, Inc.

    % Additions to classpath.txt:
    %	$matlabroot/Java3D/1.5.0/lib/ext/j3dcore.jar
    %	$matlabroot/Java3D/1.5.0/lib/ext/j3dutils.jar
    %
    % Addition to librarypath.txt:
    %	$matlabroot/Java3D/1.5.0/bin

    % Notes:
    % Java3D items are not serializable, so they must be reconstructed upon
    % load.  We use loadobj to do this.

Class Properties

    properties ( SetAccess = 'public', GetAccess = 'public' )
        active_shape_name;		% (char) Name of a shape class
    end
    properties ( SetAccess = 'public', GetAccess = 'public', Dependent = true )
        tree;                   % (JTree)
        tree_model;             % (TreeModel)
        selected_path;          % (TreePath)
        selected_node;          % (TreeNode)
        selected_shape;         % (Shape)
        selected_shape_parent;  % (Shape)
    end
    properties ( SetAccess = 'private', GetAccess = 'public' )
        body;                   % (ComplexShape)
        controls_pane;          % (ControlsPane)
        details_pane;           % (DetailsPane)
        materials;              % ({ShapeMaterial})
        navigator_pane;         % (NavigatorPane)
        viewing_pane;           % (ViewingPane)
        world_origin;           % (CoordSystem)
        log_filename;           % (char)
    end
    properties ( SetAccess = 'private', GetAccess = 'public', Constant = true )
        mass_units		= 'kg';
        length_units	= 'm';
        rotation_type	= 'Euler X-Y-Z';
        rotation_units	= 'rad';
        time_units		= 'sec';
    end
    properties ( SetAccess = 'private', GetAccess = 'private' )
        body_dir;               % (char)
        body_name;              % (char)
        frame;                  % (javax.swing.JFrame)
        is_dirty;               % (logical)
        title;                  % (char)
    end

    methods

Constructor

        function obj = BodyBuilder(varargin)
            % TBD.  The plan is to substitute the current approach to logging
            % diagnostics to a known filename (using Log.msg) to one that uses a
            % tempname generated when an instance of BodyBuilder is created.
%             if (nargin == 0) && isa(varargin{1},'logical')  && varargin{1}
%                 obj.log_filename = fullfile(tempname,'.log');
%             end
%             if ~isempty(obj.log_filename)
%                 fid = fopen(obj.log_filename,'a');
%                 fprintf(fid,sprintf('%s::%s',mfilename,mfilename));
%                 fclose(fid);
%             end

            % Attempt to delete Log file for fresh start
            try
                delete(Log.filename);
            catch %#ok<CTCH>
            end
            Log.msg(sprintf('\n%s::%s',mfilename,mfilename));

            % Load in the callback function handles
            fh = svSupport();

            % Construct Library of Materials
            % material name, density (kg/m^3), youngsmodulus (MPa), poissons ratio, ambient, emissive, diffuse, specular, (1-128)  dull -to- shiny
            materials           = cell(0);
            materials{end+1}    = ShapeMaterial('material_name','Aluminum',...
                'density',2700,...
                'youngs_modulus',68,...
                'poissons_ratio',0.33,...
                'ambientColor',[0.6602 0.6719 0.7109],...
                'emissiveColor',[0.6602 0.6719 0.7109],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Steel',...
                'density',7800,...
                'youngs_modulus',200,...
                'poissons_ratio',0.3,...
                'ambientColor',[0.4805 0.4063 0.9297],...
                'emissiveColor',[0.4805 0.4063 0.9297],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Platinum',...
                'ambientColor',[0.984 0.8008 0.9641],...
                'emissiveColor',[0.984 0.8008 0.9641],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Plastic',...
                'ambientColor',[0.1984 0.8008 0.4641],...
                'emissiveColor',[0.1984 0.8008 0.4641],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Rubber',...
                'ambientColor',[0.1984 0.1008 0.1641],...
                'emissiveColor',[0.1984 0.1008 0.1641],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Titanium',...
                'density',4500,...
                'youngs_modulus',116,...
                'poissons_ratio',0.34,...
                'ambientColor',[0.3984 0.8008 0.6641],...
                'emissiveColor',[0.3984 0.8008 0.6641],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            materials{end+1}    = ShapeMaterial('material_name','Wood',...
                'ambientColor',[0.7984 0.8008 0.2641],...
                'emissiveColor',[0.7984 0.8008 0.2641],...
                'diffuseColor',[0 0 0],...
                'specularColor',[1 1 1],...
                'shininess',100);
            obj.materials = materials;

            % Initialize default directory for body
            obj.body_dir = pwd;

            % Define the world origin.
            world_origin = CoordSystem('name','World.Origin',...
                'origin',[0 0 0],'orientation',[0 0 0],'is_deletable',false);
            world_origin.change_origin_visible(true); % This is used to place a small sphere at the origin to symbolize World Origin.
            obj.world_origin = world_origin;

            % Introduce the top-level ComplexShape
            body = ComplexShape('name','body');

            % When a shape is created, an Origin CoordSystem is created.
            % Normally, the reference_CS for an Origin CoordSystem is set when
            % the Shape is attached to a ComplexShape.  Here, we are dealing
            % with the top-level Shape, so the reference_CS is set to the world
            % origin.
            body.coord_system{1}.reference_CS = world_origin;
            obj.body = body;

            % Setting the reference_CS for a CoordSystem also links the two
            % CoordSystems in terms of behavior (the BranchGroup of
            % coord_system{1}'s Triad becomes a child of the TransformGroup of
            % world_origin's Triad).  In order to get the whole system in the
            % SceneGraph, we need to attach the BranchGroup of the
            % world_origin's Triad to body_TransformGroup, which is a property
            % of ViewingPane.  This is done in the CreateSceneGraph method of
            % ViewingPane.

            % Build the Gui
            [frame paneObjects] = obj.build_gui(fh);
            obj.frame           = frame;
            obj.controls_pane   = paneObjects{1};
            obj.viewing_pane    = paneObjects{2};
            obj.details_pane    = paneObjects{3};
            obj.navigator_pane  = paneObjects{4};

            % Add the body to the tree
            obj.navigator_pane.add_shape_to_navigatorPane(body,[]);

            % Add the block name to the title of the Gui
            obj.title = ['Body Builder: ' body.name];

            % Don't return anything if there is no LHS argument.
%             if (nargout > 0)
%                 varargout = obj;
%             end
        end

SET Methods

        function set.active_shape_name(obj, in)
            prop_name = 'active_shape_name';
            prop_type = 'char';
            [valid_flag in] = Validate.ARRAY(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.body(obj,a_body)
            if ~isempty(a_body) && ~isa(a_body,'ComplexShape')
                error('%s: set.body.  Expected a ComplexShape object',mfilename);
            else
                % Clear the body.
                obj.body = a_body;
                obj.is_dirty = false;
                obj.body_name = '';

                % Make all necessary updates to Panes.
                if ~isempty(obj.viewing_pane)
                    obj.viewing_pane.detach_body;
                end
                if ~isempty(obj.navigator_pane)
                    obj.navigator_pane.clear_tree;
                end
            end
            if isa(a_body,'ComplexShape')
                obj.body_name = [a_body.name '.body'];

                % Are we in the initialization stage?  Check to see if ViewingPane
                % and NavigatorPane have been created.
                if ~isempty(obj.viewing_pane) && ~isempty(obj.navigator_pane)
                    obj.add_shape(a_body, true); % true ==> add to top level
                end
            end
        end
        function set.body_dir(obj, in)
            prop_name = 'body_dir';
            prop_type = 'char';
            [valid_flag in] = Validate.ARRAY(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.body_name(obj, in)
            prop_name = 'body_name';
            prop_type = 'char';
            [valid_flag in] = Validate.ARRAY(in,'type',prop_type);
            if valid_flag
                % Substitute underscores for spaces
                obj.(prop_name)	= strrep(in,' ','_');
            end
        end
        function set.controls_pane(obj, in)
            prop_name = 'controls_pane';
            prop_type = 'ControlsPane';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.details_pane(obj, in)
            prop_name = 'details_pane';
            prop_type = 'DetailsPane';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.frame(obj, in)
            prop_name = 'frame';
            prop_type = 'javax.swing.JFrame';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.is_dirty(obj, in)
            prop_name = 'is_dirty';
            prop_type = 'logical';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.materials(obj, in)
            prop_name = 'materials';
            prop_type = 'ShapeMaterial';
            [valid_flag in] = Validate.ARRAY(in,'type',prop_type,'Cell',true);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.navigator_pane(obj, in)
            prop_name = 'navigator_pane';
            prop_type = 'NavigatorPane';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.title(obj, in)
            prop_name = 'title';
            prop_type = 'char';
            [valid_flag in] = Validate.ARRAY(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
                obj.frame.setTitle(in);
            end
        end
        function set.viewing_pane(obj, in)
            prop_name = 'viewing_pane';
            prop_type = 'ViewingPane';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end
        function set.world_origin(obj, in)
            prop_name = 'world_origin';
            prop_type = 'CoordSystem';
            [valid_flag in] = Validate.SCALAR(in,'type',prop_type);
            if valid_flag
                obj.(prop_name) = in;
            end
        end

GET Methods

        function tree = get.tree(obj)
%             tree = obj.navigator_pane.pane.getComponent(0); % When JPanel is used
            tree = obj.navigator_pane.pane.getComponent(0).getComponent(0); % When JScrollPane is used
        end
        function tree_model = get.tree_model(obj)
            tree_model = obj.tree.getModel;
        end
        function selected_path = get.selected_path(obj)
            selected_path = obj.tree.getSelectionPath;
        end
        function selected_node = get.selected_node(obj)
            selected_node = obj.selected_path.getLastPathComponent;
        end
        function selected_shape = get.selected_shape(obj)
            selected_path  = obj.selected_path;
            hierarchy = selected_path.toString.toCharArray;
            hierarchy = hierarchy(2:end-1)';
            % The hierarchy, which is a comma delimited char array provides the
            % linkage top down from the Body to the selected shape.

            % Create a cell array (shape_hierarchy) from the char array
            % (hierarchy).  The format of shape_hierarchy is a cell array such
            % as {'Car','Frame','Front_Axle'}.
            pattern = '([^\s,][^,]+)';
            shape_hierarchy = regexp(hierarchy,pattern,'tokens');

            % March down to the selected shape.
            current_shape = obj.body;
            for ii = 1:length(shape_hierarchy)-1
                % Find matching Shape name within the current parent's array of
                % Shapes
                parent_shape = current_shape;

                current_shape = parent_shape.retrieve_child_from_name(shape_hierarchy{ii+1});
            end
            selected_shape = current_shape;
        end
        function selected_shape_parent = get.selected_shape_parent(obj)
            selected_path  = obj.selected_path;
            hierarchy = selected_path.toString.toCharArray;
            hierarchy = hierarchy(2:end-1)';
            % The hierarchy, which is a comma delimited char array provides the
            % linkage top down from the Body to the selected shape.

            % Create a cell array (shape_hierarchy) from the char array
            % (hierarchy).  The format of shape_hierarchy is a cell array such
            % as {'Car','Frame','Front_Axle'}.
            pattern = '([^\s,][^,]+)';
            shape_hierarchy = regexp(hierarchy,pattern,'tokens');

            % March down to the selected shape.
            current_shape = obj.body;
            parent_shape = current_shape;
            for ii = 1:length(shape_hierarchy)-2
                % Find matching Shape name within the current parent's array of
                % Shapes
                current_shape = parent_shape.retrieve_child_from_name(shape_hierarchy{ii+1});
                parent_shape = current_shape;
            end
            selected_shape_parent = parent_shape;
        end

Public HELPER Methods

        function add_shape(obj, the_shape, topLevel_flag)
			if isempty(the_shape)
				% No shape for some reason.  Should never reach this point.
				error('%s: add_shape.  Expected to add shape to %s, but none provided',mfilename, obj.active_shape_name);
			else
				if nargin > 2 && topLevel_flag
					% Need to add to top level
					the_shape.coord_system{1}.reference_CS = obj.world_origin;
					for ii = 1:the_shape.n_coord_system
						the_shape.coord_system{ii}.triad.length = obj.world_origin.triad.length;
					end
				else
					parent_shape = obj.selected_shape;
					if isa(parent_shape,'PrimitiveShape')
						% Warn that parent is a primitive shape if that is the case
						warning('Can only add Shapes to Shape Assemblies'); %#ok<WNTAG>
						return
					else
						% This fully defines the Shape's first CoordSystem
						the_shape.coord_system{1}.reference_CS = parent_shape.coord_system{1};
						for ii = 1:the_shape.n_coord_system
							the_shape.coord_system{ii}.triad.length = obj.world_origin.triad.length;
						end
						% Update the Body
						parent_shape.add_child(the_shape);
					end
				end
			end
            % Update dirty status
            obj.is_dirty = true;

            % Update the Navigator pane
            obj.navigator_pane.add_shape_to_navigatorPane(the_shape,[]);
        end

        function remove_shape(obj, the_shape)
            % Remove the_shape from its parent shape.
            the_parent_shape = obj.selected_shape_parent;
            the_parent_shape.remove_child(the_shape);

            % Remove the_shape from NavigatorPane
            the_parent_path		= obj.selected_path.getParentPath;
            node_to_remove		= obj.selected_node;
            obj.tree.getModel.removeNodeFromParent(node_to_remove);

            % Select the parent
            obj.tree.setSelectionPath(the_parent_path);

            % Remove the_shape from ViewingPane by detaching the_shape's
            % BranchGroup from its parent's TransformGroup.
            the_BranchGroup		= the_shape.BranchGroup;
            the_TransformGroup	= the_BranchGroup.getParent;
            for ii = 0:the_TransformGroup.numChildren-1
                if the_TransformGroup.getChild(ii).equals(the_BranchGroup)
                    the_TransformGroup.removeChild(ii);
                    break
                end
            end

            % Delete the_shape and everything underneath.
            delete(the_shape);
        end

        function change_shape_name(obj, tree)
            selected_path   = tree.getSelectionPath;
            selected_node   = selected_path.getLastPathComponent;
            the_parent      = selected_node.getParent;
			if(isempty(the_parent))
				% This is the body
				the_child = obj.body;
			else
				for ii = 1:the_parent.getChildCount
					if the_parent.getChildAt(ii-1) == selected_node
						idx = ii;
						break;
					end
				end
				the_parent_shape    = obj.selected_shape_parent;
				the_child           = the_parent_shape.children{idx};
			end
            child_name          = selected_node.toString.toCharArray;
            the_child.name      = child_name(:)';
        end

        function add_materials(obj,a_material)
            the_materials = obj.materials;
            the_materials{end+1} = a_material;
            obj.materials = the_materials;
        end

        function append_body_materials(obj,the_body)
            % Check for new materials and add to Material library if necessary
            % TBD
        end

        function body = open_body(obj)
            body = [];
            [pathstr, name, ext] = fileparts(obj.body_name);
            file_filter = [obj.body_dir '\*' ext];
            [filename, pathname] = uigetfile(file_filter, 'Select the Body File');
            if ~isequal(filename,0)
                try
                    obj.body_dir = pathname;
                    tmp = load(fullfile(pathname,filename),'-mat'); % Returns body
                    % Get the name of the body and store its contents in body
                    if isstruct(tmp)
                        fn = fieldnames(tmp);
                        if ~isempty(fn)
                            body_candidate = tmp.(fn{1});
                            if isa(body_candidate,'ComplexShape')
                                body = body_candidate;
                            end
                        end
                    end
                catch ME
                    % Get last segment of the error message identifier.
                    idSegLast = regexp(ME.identifier, '(?<=:)\w+$', 'match');
                    error(idSegLast{:});
                end
            end
        end

        function status = check_to_save(the_bodyBuilder)
            % See if session is dirty
            if the_bodyBuilder.is_dirty
                question = sprintf('Do you want to save the changes made to %s?',the_bodyBuilder.body_name);
                dlg	= javaObjectEDT('javax.swing.JOptionPane');
                status = dlg.showConfirmDialog(javaObjectEDT('javax.swing.JFrame'), ...
                    question,'Exit',dlg.YES_NO_CANCEL_OPTION);
            else
                status = [];
            end
        end

        function export_to_xml(obj, the_body, xmlFilePath)
            % Create a DOM node for the .xml document that will be imported
            % using import_physmod.

            name = ['"' the_body.name '"'];
            the_coord_systems = the_body.coord_system;

            % <?xml version="1.0"?>
            doc_Node	= com.mathworks.xml.XMLUtils.createDocument('PhysicalModelingXMLFile');
            % doc_Node (Document interface) represents the entire XML
            % document. Conceptually, it is the root of the document tree,
            % and provides the primary access to the document's data.
            root_Node	= doc_Node.getDocumentElement;

            % <formatVersion>
            thisElement	= doc_Node.createElement('formatVersion');
            thisElement.appendChild(doc_Node.createTextNode('"1.0.2"'));
            root_Node.appendChild(thisElement);

            % <creationDate>
            thisElement	= doc_Node.createElement('creationDate');

            thisElement.appendChild(doc_Node.createTextNode(datestr(now)));
            root_Node.appendChild(thisElement);

            % <createdBy>
            thisElement	= doc_Node.createElement('createdBy');
            thisElement.appendChild(doc_Node.createTextNode(getenv('user')));
            root_Node.appendChild(thisElement);

            % <modelName>
            thisElement = doc_Node.createElement('modelName');
            thisElement.appendChild(doc_Node.createTextNode(name));
            root_Node.appendChild(thisElement);

            % <subsystem>
            subsystemElement = doc_Node.createElement('subsystem');
            root_Node.appendChild(subsystemElement);

            % <Subsystem>
            SubsystemElement = doc_Node.createElement('Subsystem');
            subsystemElement.appendChild(SubsystemElement);

            % <name>
            thisElement = doc_Node.createElement('name');
            thisElement.appendChild(doc_Node.createTextNode(name));
            SubsystemElement.appendChild(thisElement);

            % <status>
            thisElement = doc_Node.createElement('status');
            thisElement.appendChild(doc_Node.createTextNode(''));
            SubsystemElement.appendChild(thisElement);

            % <bodies>
            bodiesElement = doc_Node.createElement('bodies');
            SubsystemElement.appendChild(bodiesElement);

            % <Body>
            BodyElement = doc_Node.createElement('Body');
            bodiesElement.appendChild(BodyElement);

            % <name>
            thisElement = doc_Node.createElement('name');
            thisElement.appendChild(doc_Node.createTextNode(name));
            BodyElement.appendChild(thisElement);

            % <status>
            thisElement = doc_Node.createElement('status');
            thisElement.appendChild(doc_Node.createTextNode('""'));
            BodyElement.appendChild(thisElement);

            % <mass>
            thisElement = doc_Node.createElement('mass');
            thisElement.appendChild(doc_Node.createTextNode(num2str(the_body.mass))); % E.g. num2str(the_body.mass)
            BodyElement.appendChild(thisElement);

            % <massUnits>
            thisElement = doc_Node.createElement('massUnits');
            thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',BodyBuilder.mass_units))); % E.g. "kg"
            BodyElement.appendChild(thisElement);

            % <inertia>
            thisElement = doc_Node.createElement('inertia');
            inertia_matrix = the_body.moment_of_inertia_about_CG;
            thisElement.appendChild(doc_Node.createTextNode(sprintf('%f,%f,%f,%f,%f,%f,%f,%f,%f',inertia_matrix')));
            BodyElement.appendChild(thisElement);

            % <inertiaUnits>
            thisElement = doc_Node.createElement('inertiaUnits');
            thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s*%s^2"',BodyBuilder.mass_units,BodyBuilder.length_units)));
            BodyElement.appendChild(thisElement);

            % <volume>
            thisElement = doc_Node.createElement('volume');
            thisElement.appendChild(doc_Node.createTextNode(num2str(the_body.volume)));
            BodyElement.appendChild(thisElement);

            % <volumeUnits>
            thisElement = doc_Node.createElement('volumeUnits');
            thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s^3"',BodyBuilder.length_units)));
            BodyElement.appendChild(thisElement);

            % <surfaceArea>
            thisElement = doc_Node.createElement('surfaceArea');
            thisElement.appendChild(doc_Node.createTextNode(num2str(1))); % E.g. 0
            BodyElement.appendChild(thisElement);

            % <surfaceAreaUnits>
            thisElement = doc_Node.createElement('surfaceAreaUnits');
            thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s^2"',BodyBuilder.length_units)));
            BodyElement.appendChild(thisElement);

            % <frames>
            framesElement = doc_Node.createElement('frames');
            BodyElement.appendChild(framesElement);

			% If the selected body is a complex shape, also add CG to the
			% list.
			if(isa(the_body,'ComplexShape'))
				cs = the_body.center_of_mass;
				% <Frame>
                FrameElement = doc_Node.createElement('Frame');
                framesElement.appendChild(FrameElement);

                % <name>
                thisElement = doc_Node.createElement('name');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"','CG'))); % E.g. "CSO", "CG"
                FrameElement.appendChild(thisElement);

                % <position>
                thisElement = doc_Node.createElement('position');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('%f,%f,%f',cs.origin))); % E.g. 0,0,0
                FrameElement.appendChild(thisElement);

                % <positionOrigin>
                thisElement = doc_Node.createElement('positionOrigin');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(cs.reference_CS.name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);

                % <positionReferenceFrame>
                thisElement = doc_Node.createElement('positionReferenceFrame');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(cs.reference_CS.name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);

                % <positionUnits>
                thisElement = doc_Node.createElement('positionUnits');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',BodyBuilder.length_units))); % E.g. "m"
                FrameElement.appendChild(thisElement);

                % <orientation>
                thisElement = doc_Node.createElement('orientation');
                rotation_matrix = CoordSystem.EulerXYZ_to_DirCosineMatrix(cs.orientation);
                thisElement.appendChild(doc_Node.createTextNode(sprintf('%f,%f,%f,%f,%f,%f,%f,%f,%f',rotation_matrix))); % E.g. 1,0,0,0,1,0,0,0,1
                FrameElement.appendChild(thisElement);

                % <orientationType>
                thisElement = doc_Node.createElement('orientationType');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"3x3 Transform"'))); % E.g. "Euler X-Y-Z"
                FrameElement.appendChild(thisElement);

                % <orientationUnits>
                thisElement = doc_Node.createElement('orientationUnits');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',BodyBuilder.rotation_units))); % E.g. "rad"
                FrameElement.appendChild(thisElement);

                % <orientationReferenceFrame>
                thisElement = doc_Node.createElement('orientationReferenceFrame');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(cs.reference_CS.name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);

			end

            for ii = 1:length(the_coord_systems)
                cs			= the_coord_systems{ii};
				cs_name = cs.name;

				% Note: Here we are only calculating coordinate values as if
				% the reference CS is changed. We do not actually change
				% the reference CS. This is because after exporting, we
				% still want to maintain the reference CSs as given by the
				% user and continue with his bodybuilding:)
				if(strcmp(cs.name,'Origin'))
					% origin CS is always w.r.to its parent's origin. So
					% calculate the CS coordinates so that its new reference CS is
					% World Origin.
					[cs_origin cs_orientation] = cs.convert_local_coordinates_to_world;
					ref_CS_name = 'World.Origin';
				elseif(~isequal(cs.parent_shape,cs.reference_CS.parent_shape))
					[cs_origin cs_orientation] = cs.convert_coordinates_wrt_new_refCS(the_coord_systems{1});
					ref_CS_name = 'Origin';
				else
					cs_origin = cs.origin;
					cs_orientation = cs.orientation;
					ref_CS_name = cs.reference_CS.name;
				end

                % <Frame>
                FrameElement = doc_Node.createElement('Frame');
                framesElement.appendChild(FrameElement);

                % <name>
                thisElement = doc_Node.createElement('name');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(cs_name)))); % E.g. "CSO", "CG"
                FrameElement.appendChild(thisElement);

                % <position>
                thisElement = doc_Node.createElement('position');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('%f,%f,%f',cs_origin))); % E.g. 0,0,0
                FrameElement.appendChild(thisElement);

                % <positionOrigin>
                thisElement = doc_Node.createElement('positionOrigin');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(ref_CS_name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);

                % <positionReferenceFrame>
                thisElement = doc_Node.createElement('positionReferenceFrame');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(ref_CS_name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);

                % <positionUnits>
                thisElement = doc_Node.createElement('positionUnits');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',BodyBuilder.length_units))); % E.g. "m"
                FrameElement.appendChild(thisElement);

                % <orientation>
                thisElement = doc_Node.createElement('orientation');
                rotation_matrix = CoordSystem.EulerXYZ_to_DirCosineMatrix(cs_orientation);
                thisElement.appendChild(doc_Node.createTextNode(sprintf('%f,%f,%f,%f,%f,%f,%f,%f,%f',rotation_matrix))); % E.g. 1,0,0,0,1,0,0,0,1
                FrameElement.appendChild(thisElement);

                % <orientationType>
                thisElement = doc_Node.createElement('orientationType');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"3x3 Transform"'))); % E.g. "Euler X-Y-Z"
                FrameElement.appendChild(thisElement);

                % <orientationUnits>
                thisElement = doc_Node.createElement('orientationUnits');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',BodyBuilder.rotation_units))); % E.g. "rad"
                FrameElement.appendChild(thisElement);

                % <orientationReferenceFrame>
                thisElement = doc_Node.createElement('orientationReferenceFrame');
                thisElement.appendChild(doc_Node.createTextNode(sprintf('"%s"',obj.convert_name(ref_CS_name)))); % E.g. "WORLD"
                FrameElement.appendChild(thisElement);
            end

            % <connectionPorts>
            thisElement = doc_Node.createElement('connectionPorts');
            SubsystemElement.appendChild(thisElement);

            % <id>
            thisElement = doc_Node.createElement('id');
            thisElement.appendChild(doc_Node.createTextNode(num2str(0))); % E.g. 0
            SubsystemElement.appendChild(thisElement);

            xmlwrite(xmlFilePath,doc_Node);
        end

        function export_to_stl(obj, the_body, stlFilePath)
            % Export to .stl document that will be available for use within
            % SimMechanics.
            %   solid robot - forearm-1
            %       facet normal -8.742778e-001 0.000000e+000 4.854259e-001
            %           outer loop
            %               vertex 1.687836e-001 1.489590e-001 8.808903e-004
            %               vertex 1.686560e-001 1.489590e-001 6.350000e-004
            %               vertex 1.687836e-001 1.483240e-001 8.808903e-004
            %           endloop
            %       endfacet
            %   endsolid

            [vertexInfo normalInfo] = the_body.get_vertex_info;

            fid = fopen(stlFilePath, 'wt');
            fprintf(fid, 'solid %s\n', the_body.name);
            for ii = 1:n_facets
                fprintf(fid, '\tfacet normal %13.5e\n', normalInfo(ii,:));
                fprintf(fid, '\t\touter loop\n');
                for jj = 1:3
                    fprintf(fid, '\t\t\tvertex %13.5e\n', vertexInfo(jj,:));
                end
                fprintf(fid, '\t\tendloop\n');
                fprintf(fid, '\tendfacet\n');
            end
            fprintf(fid, 'endsolid\n');
            fclose(fid)
        end
    end

    methods ( Access = 'private' )

Private HELPER Methods

        function [frame paneObjects] = build_gui(obj,fh)
            % Create Frame
            frame = javaObjectEDT('javax.swing.JFrame');

            % Position the Frame
            set(0,'Units','pixels');
            screenSize		= get(0,'ScreenSize');
            topMargin		= 10;
            bottomMargin	= 10;
            leftMargin		= 10;
            rightMargin		= 10;
            width			= screenSize(3) - leftMargin - rightMargin;
            height			= screenSize(4) - topMargin - bottomMargin;
            frame.setBounds(leftMargin, topMargin, width, height);

            % Add the Icon
            icon_file = fullfile(matlabroot,'toolbox','matlab','icons','matlabicon.gif');
            icon = javaObjectEDT('javax.swing.ImageIcon',icon_file);
            frame.setIconImage(icon.getImage());

            % Create Menu bar
            obj.create_menu_bar(frame,fh);

            % Create panes
            cp = frame.getContentPane;
            cp.setLayout(java.awt.GridBagLayout);

            paneObjects{1} = ControlsPane(obj);
            paneObjects{2} = ViewingPane(obj);
            paneObjects{3} = DetailsPane(obj);
            paneObjects{4} = NavigatorPane(obj);

            pane_size_pref_width     = [50 150 150 50];
            pane_size_pref_height    = [100 300 200 400];
            panes		= cell(length(paneObjects),1);
            for ii = 1:length(paneObjects)
                panes{ii} = paneObjects{ii}.pane();
                panes{ii}.setPreferredSize(...
                    java.awt.Dimension(pane_size_pref_width(ii),pane_size_pref_height(ii)))
            end

            % Add panes to frame
            % Preferred layout dimensions
            pref.gridx		= [0 1 1 0];
            pref.gridy		= [0 0 3 1];
            pref.gridwidth	= [1 3 3 1];
            pref.gridheight = [1 3 2 4];
            pref.weightx	= [1 5 5 1];
            pref.weighty	= [0 3 0 4];

            gb = javaObjectEDT('java.awt.GridBagConstraints');
            gb.fill = gb.BOTH;	% components grow in both dimensions
            gb.insets = java.awt.Insets(5, 5, 5, 5);		% 5-pixel margins on all sides
            for ii = 1:length(panes)
                gb.gridx		= pref.gridx(ii);
                gb.gridy		= pref.gridy(ii);
                gb.gridheight	= pref.gridheight(ii);
                gb.gridwidth	= pref.gridwidth(ii);
                gb.weightx		= pref.weightx(ii);
                gb.weighty		= pref.weighty(ii);
                cp.add(panes{ii}, gb);
            end

            % Update the frame and show it
            javax.swing.UIManager.setLookAndFeel( ...
                javax.swing.UIManager.getSystemLookAndFeelClassName());
            javax.swing.SwingUtilities.updateComponentTreeUI(frame);

            % 			frame.pack
            frame.setVisible(true)
        end

        function menu_bar = create_menu_bar(obj, frame, fh)
            import java.awt.event.*;

            menu_bar = javaObjectEDT('javax.swing.JMenuBar');
            frame.setJMenuBar(menu_bar);

            % =====================================================
            % Create a File menu
            file_menu = javaObjectEDT('javax.swing.JMenu','File');
            file_menu.setMnemonic('F');
            menu_bar.add(file_menu);

            % New Body
            BodyBuilder.add_menu_item(file_menu,'New Body',...
                {fh.new_body_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_N,KeyEvent.VK_N);

            % Open Body
            BodyBuilder.add_menu_item(file_menu,'Open Body',...
                {fh.open_body_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_O,KeyEvent.VK_O);

%             % Import Body From VRML
%             BodyBuilder.add_menu_item(file_menu,'(TBD)Import Body from VRML',...
%                 {fh.importVRML_ActionPerformedCallback,obj,frame},...
%                 KeyEvent.VK_I,KeyEvent.VK_I);

            % Separator
            file_menu.addSeparator();

            % Save Body
            BodyBuilder.add_menu_item(file_menu,'Save Body',...
                {fh.save_body_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_S,KeyEvent.VK_S);

            % Save Body As...
            BodyBuilder.add_menu_item(file_menu,'Save Body As..',...
                {fh.save_body_as_ActionPerformedCallback,obj,frame});

            % Save Selected Body As...
            BodyBuilder.add_menu_item(file_menu,'Save Selected Body As..',...
                {fh.save_selected_body_as_ActionPerformedCallback,obj,frame});

            % Separator
            file_menu.addSeparator();

            % Close
            BodyBuilder.add_menu_item(file_menu,'Close Body Builder',...
                {fh.close_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_C,KeyEvent.VK_C);


            % =====================================================
            % Create a Edit menu
            edit_menu = javaObjectEDT('javax.swing.JMenu','Edit');
            edit_menu.setMnemonic('E');
            menu_bar.add(edit_menu);

            % Attach a Body
            BodyBuilder.add_menu_item(edit_menu,'Attach a Body',...
                {fh.attach_body_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_A,KeyEvent.VK_A);

            % =====================================================
            % Create a View menu
            view_menu = javaObjectEDT('javax.swing.JMenu','View');
            view_menu.setMnemonic('V');
            menu_bar.add(view_menu);

            % World Axes Display Setting
            BodyBuilder.add_menu_item(view_menu,'Toggle World Axes Display',...
                {fh.worldAxesDisplay_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_W,KeyEvent.VK_W);

            % Body Axes Display Setting
            BodyBuilder.add_menu_item(view_menu,'Toggle Body Axes Display',...
                {fh.bodyAxesDisplay_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_B,KeyEvent.VK_B);

            % Separator
            view_menu.addSeparator();

            % Axes Length Setting
            BodyBuilder.add_menu_item(view_menu,'Set Axes Length',...
                {fh.axesLength_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_L,KeyEvent.VK_L);

            % =====================================================
            % Create a Tools menu
            tools_menu = javaObjectEDT('javax.swing.JMenu','Tools');
            tools_menu.setMnemonic('T');
            menu_bar.add(tools_menu);

            % Export to SimMechanics
            BodyBuilder.add_menu_item(tools_menu,'Export to SimMechanics',...
                {fh.export2SM_ActionPerformedCallback,obj,frame},...
                KeyEvent.VK_E,KeyEvent.VK_E);

            % Export Selected Body to SimMechanics
            BodyBuilder.add_menu_item(tools_menu,'Export Selected Body to SimMechanics',...
                {fh.exportSelBody2SM_ActionPerformedCallback,obj,frame});

            % Separator
            tools_menu.addSeparator();

            % Export to XML
            BodyBuilder.add_menu_item(tools_menu,'Export to XML',...
                {fh.export2XML_ActionPerformedCallback,obj,frame});

            % Export Selected Body to XML
            BodyBuilder.add_menu_item(tools_menu,'Export Selected Body to XML',...
                {fh.exportSelBody2XML_ActionPerformedCallback,obj,frame});

            % Separator
%             tools_menu.addSeparator();

            % Export to STL File
%             BodyBuilder.add_menu_item(tools_menu,'Export to STL',...
%                 {fh.export2STL_ActionPerformedCallback,obj,frame});

            % Export Selected Body to STL
%             BodyBuilder.add_menu_item(tools_menu,'Export Selected Body to STL',...
%                 {fh.exportSelBody2STL_ActionPerformedCallback,obj,frame});

             % Separator
%             tools_menu.addSeparator();

             % Export to VRML
%             BodyBuilder.add_menu_item(tools_menu,'Export to VRML',...
%                 {fh.export2VRML_ActionPerformedCallback,obj,frame},...
%                 KeyEvent.VK_V,KeyEvent.VK_V);
%
             % Export Selected Body to VRML
%             BodyBuilder.add_menu_item(tools_menu,'Export to VRML',...
%                 {fh.exportSelBody2VRML_ActionPerformedCallback,obj,frame});

            % Separator
            tools_menu.addSeparator();

            % Display CoordSystem Ancestry
            BodyBuilder.add_menu_item(tools_menu,'Display CoordSystem Ancestry',...
                {fh.display_cs_ancestry,obj,frame});

%             % DEBUG
%             BodyBuilder.add_menu_item(tools_menu,'DEBUG',...
%                 {fh.debug_ActionPerformedCallback,obj,frame},...
%                 KeyEvent.VK_D,KeyEvent.VK_D);

            % =====================================================
            % Create a Help menu
            help_menu = javaObjectEDT('javax.swing.JMenu','Help');
            help_menu.setMnemonic('H');
            menu_bar.add(help_menu);

            % Help (F1)
%             BodyBuilder.add_menu_item(help_menu,'Help with Body Builder',...
%                 {fh.help_ActionPerformedCallback,obj,frame},...
%                 KeyEvent.VK_H,KeyEvent.VK_H);

            menu_item = javaObjectEDT('javax.swing.JMenuItem','Help with Body Builder');
            menu_item.setAccelerator(javax.swing.KeyStroke.getKeyStroke('F1'));
            set(handle(menu_item, 'CallbackProperties'),'ActionPerformedCallback',{fh.help_ActionPerformedCallback,obj,frame});
            help_menu.add(menu_item);
        end

        function new_name = convert_name(obj,cur_name)
            % To stay within the convention of SimMechanics, we need to
            % rename the_body's origin to CS0 and World.Origin to
            % WORLD.

            switch cur_name
                case obj.world_origin.name
                    new_name = 'World';
				case 'Origin'
                % case [obj.body.name '.Origin']
                    new_name = 'CS0';
                otherwise
                    % [tmp dot_name] = strtok(cur_name,'.');
                    % new_name = dot_name(2:end);
					new_name = cur_name;
            end
        end
    end

    methods ( Access = 'private', Static = true )

Static Private HELPER Methods

        function add_menu_item(menu,label,callback,mnemonic,accelerator)
            if nargin > 3
                % Then set a mnemonic for the menu item
                menu_item = javaObjectEDT('javax.swing.JMenuItem',label,mnemonic);

                if nargin > 4
                    % Then set an accelerator for the menu item
                    keystroke = javax.swing.KeyStroke.getKeyStroke( ...
                        accelerator,java.awt.event.ActionEvent.CTRL_MASK);
                    menu_item.setAccelerator(keystroke)
                end
            else
                menu_item = javaObjectEDT('javax.swing.JMenuItem',label);
            end

            set(handle(menu_item, 'CallbackProperties'),'ActionPerformedCallback',callback);
            menu.add(menu_item);
        end
    end
end