Contents
Triad
This class enables 3D axes display.
classdef Triad < handle
% This defines the Triad class. % URL : $URL: $ % Log : $Id: Triad.html,v 1.1 2008/07/23 12:51:20 jberg Exp $ % Copyright (c) 2008 The MathWorks, Inc.
Class Properties
properties ( SetAccess = 'public', GetAccess = 'public' ) is_origin_visible = false; % (logical) Represents the visibility of a small sphere at the origin of the triad is_semi_length = true; % (logical) true -> display axes from origin out; false -> display axes on both sides of origin. is_visible = true; % (logical) length = 0.25; % (pos semidef double) name = 'New Triad'; % (char) transparency = 0.25; % (pos semidef double) end properties ( SetAccess = 'private', GetAccess = 'public' ) BranchGroup; % (javax.media.j3d.BranchGroup) TransformGroup; % (javax.media.j3d.TransformGroup) BG_parent_str = ''; % (String) TG_id_str = ''; % (String) end properties ( SetAccess = 'private', GetAccess = 'private' ) primitives; % (com.sun.j3d.utils.geometry...) axis_x_TG % (javax.media.j3d.TransformGroup) axis_y_TG % (javax.media.j3d.TransformGroup) axis_z_TG % (javax.media.j3d.TransformGroup) axis_T3D % (javax.media.j3d.Transform3D) axis_x_T3D % (javax.media.j3d.Transform3D) axis_y_T3D % (javax.media.j3d.Transform3D) axis_z_T3D % (javax.media.j3d.Transform3D) axis_offset_V3f % (javax.vecmath.Vector3f) arrow_x_TG % (javax.media.j3d.TransformGroup) arrow_y_TG % (javax.media.j3d.TransformGroup) arrow_z_TG % (javax.media.j3d.TransformGroup) arrow_T3D % (javax.media.j3d.Transform3D) arrow_offset_V3f % (javax.vecmath.Vector3f) end properties ( SetAccess = 'private', GetAccess = 'private', Dependent = true ) arrow_radius; % (double) arrow_length; % (double) axes_radius; % (double) n_primitives; % (int) origin_sphere_radius;% (double) end properties ( SetAccess = 'private', GetAccess = 'private', Constant = true ) PROPERTY_LIST = { 'is_origin_visible' 'is_semi_length' 'is_visible' 'length' 'name' 'transparency' }; end methods
Constructor
function obj = Triad(varargin) Log.msg(sprintf('%s::%s',mfilename,mfilename)); prop_list = eval([mfilename '.PROPERTY_LIST']); n_props = length(prop_list); %#ok<CPROP> if(mod(nargin,2)~=0) error('Expecting inputs as property name-value pairs.') end error(nargchk(0, 2*n_props, nargin,'string')); % Property Name-Value pair matching for ii=1:2:nargin prop = varargin{ii}; val = varargin{ii+1}; idx = strmatch(lower(prop),lower(prop_list),'exact'); if isempty(idx) error('%s: This property is unknown',... 'or cannot be set during the construction',... 'of the object: %s ',prop,mfilename); else obj.(prop_list{idx}) = val; end end obj.add_BranchGroup; obj.add_TransformGroup; obj.add_primitives; end
SAVEOBJ Method
function A = saveobj(obj) % Java3D items are not serializable, so they must be stored another % way. For an explanation, refer to the notes at the top of % BodyBuilder.m Log.msg(sprintf('\n%s::%s',mfilename,'saveobj (Entering)')); Log.msg(sprintf('obj.name = %s',obj.name)); if ~isempty(obj.BranchGroup) the_parent = obj.BranchGroup.getParent; if ~isempty(the_parent) A.BG_parent_str = the_parent.toString.toCharArray'; Log.msg(sprintf('A.BG_parent_str = %s',A.BG_parent_str)); end end if ~isempty(obj.TransformGroup) A.TG_id_str = obj.TransformGroup.toString.toCharArray'; Log.msg(sprintf('A.TG_id_str = %s',A.TG_id_str)); end A.is_origin_visible = obj.is_origin_visible; A.is_semi_length = obj.is_semi_length; A.is_visible = obj.is_visible; A.length = obj.length; A.name = obj.name; A.transparency = obj.transparency; A.BranchGroup = []; A.TransformGroup = []; % (javax.media.j3d.TransformGroup) A.primitives = []; A.axis_x_TG = []; % (javax.media.j3d.TransformGroup) A.axis_y_TG = []; % (javax.media.j3d.TransformGroup) A.axis_z_TG = []; % (javax.media.j3d.TransformGroup) A.axis_T3D = []; % (javax.media.j3d.Transform3D) A.axis_x_T3D = []; % (javax.media.j3d.Transform3D) A.axis_y_T3D = []; % (javax.media.j3d.Transform3D) A.axis_z_T3D = []; % (javax.media.j3d.Transform3D) A.axis_offset_V3f = []; % (javax.vecmath.Vector3f) A.arrow_x_TG = []; % (javax.media.j3d.TransformGroup) A.arrow_y_TG = []; % (javax.media.j3d.TransformGroup) A.arrow_z_TG = []; % (javax.media.j3d.TransformGroup) A.arrow_T3D = []; % (javax.media.j3d.Transform3D) A.arrow_offset_V3f = []; % (javax.vecmath.Vector3f) Log.msg(sprintf('%s::%s',mfilename,'saveobj (Leaving)')); end
SET Methods
function set.BranchGroup(obj, in) % This property contains unserializable object(s), which creates a % problem for saving and loading. As a workaround, this set will % first check for the keyword 'deflate', which indicates that the % user wishes to substitute the object currently stored in the % property with a description of how to recreate the object(s). The % description will be in the form of a structure with fields cmd and % args. If the property is currently empty, it remains empty (no % harm, no foul). prop_name = 'BranchGroup'; prop_type = 'javax.media.j3d.BranchGroup'; % Validate and assign [valid_flag in] = Validate.SCALAR(in,'type',prop_type); if valid_flag obj.(prop_name) = in; end end function set.is_origin_visible(obj, in) prop_name = 'is_origin_visible'; prop_type = 'logical'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type); if valid_flag obj.(prop_name) = in; import javax.media.j3d.*; % Set visibility by setting transparency. if in % If visible, use transparency. the_transparency = obj.transparency; else % Otherwise, use 1. the_transparency = 1; end the_primitives = obj.primitives; % The first item is origin if ~isempty(the_primitives) a_transparencyAttribute = ... TransparencyAttributes(TransparencyAttributes.NICEST,the_transparency); the_appearance = the_primitives{1}.getAppearance; the_appearance.setTransparencyAttributes(a_transparencyAttribute); end end end function set.is_semi_length(obj, in) prop_name = 'is_semi_length'; prop_type = 'logical'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type); if valid_flag obj.(prop_name) = in; obj.is_semi_length = in; end end function set.is_visible(obj, in) prop_name = 'is_visible'; prop_type = 'logical'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type); if valid_flag obj.(prop_name) = in; import javax.media.j3d.*; the_primitives = obj.primitives; if isempty(the_primitives) return; % Object still under construction end % Set visibility by setting transparency. if in % If visible, use transparency. the_transparency = obj.transparency; if obj.is_origin_visible the_origin_transparency = obj.transparency; else the_origin_transparency = 1; end else % Otherwise, use 1. the_transparency = 1; the_origin_transparency = the_transparency; end the_transparencyAttribute = ... TransparencyAttributes(TransparencyAttributes.NICEST,the_transparency); the_origin_transparencyAttribute = ... TransparencyAttributes(TransparencyAttributes.NICEST,the_origin_transparency); % The first primitive is the origin, which has its own % visibility property the_appearance = the_primitives{1}.getAppearance; the_appearance.setTransparencyAttributes(the_origin_transparencyAttribute); for ii = 2:obj.n_primitives the_appearance = the_primitives{ii}.getAppearance; the_appearance.setTransparencyAttributes(the_transparencyAttribute); end end end function set.length(obj, in) prop_name = 'length'; prop_type = 'double'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type,'minVal',0); if valid_flag obj.(prop_name) = in; if obj.n_primitives > 0 obj.set_primitive_geometries; end end end function set.name(obj, in) prop_name = 'name'; prop_type = 'char'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type); if valid_flag obj.(prop_name) = in; end end function set.primitives(obj, in) % This property contains unserializable object(s), which creates a % problem for saving and loading. As a workaround, this set will % first check for the keyword 'deflate', which indicates that the % user wishes to substitute the object currently stored in the % property with a description of how to recreate the object(s). The % description will be in the form of a structure with fields cmd and % args. If the property is currently empty, it remains empty (no % harm, no foul). prop_name = 'primitives'; obj.(prop_name) = in; end function set.TransformGroup(obj, in) % This property contains unserializable object(s), which creates a % problem for saving and loading. As a workaround, this set will % first check for the keyword 'deflate', which indicates that the % user wishes to substitute the object currently stored in the % property with a description of how to recreate the object(s). The % description will be in the form of a structure with fields cmd and % args. If the property is currently empty, it remains empty (no % harm, no foul). prop_name = 'TransformGroup'; prop_type = 'javax.media.j3d.TransformGroup'; % Validate and assign [valid_flag in] = Validate.SCALAR(in,'type',prop_type); if valid_flag obj.(prop_name) = in; end end function set.transparency(obj, in) prop_name = 'transparency'; prop_type = 'double'; [valid_flag in] = Validate.ARRAY(in,'type',prop_type,'minVal',0); if valid_flag obj.(prop_name) = in; end end
GET Methods
function arrow_radius = get.arrow_radius(obj) arrow_radius = 4*obj.axes_radius; end function arrow_length = get.arrow_length(obj) arrow_length = 2*obj.arrow_radius; end function axes_radius = get.axes_radius(obj) axes_radius = obj.length/50; end function n = get.n_primitives(obj) n = length(obj.primitives); end function origin_sphere_radius = get.origin_sphere_radius(obj) origin_sphere_radius = obj.arrow_radius; end
Public HELPER Methods
function attach_BranchGroup(obj,the_parent_TG) % This method is used to attach the Triad's BranchGroup to a parent TransformGroup. Log.msg(sprintf('\n%s::%s',mfilename,'attach_BranchGroup (Entering)')); obj.BranchGroup.detach; the_parent_TG.addChild(obj.BranchGroup); obj.BG_parent_str = the_parent_TG.toString.toCharArray'; end function highlight(obj,highlighted) % This method highlights the given triad primitives. Presumably % because the associated coordinate system has been selected. if highlighted red_appearance = sv_Appearance.create_appearance(... [1 1 1],[1 1 1],[],[],150,0); green_appearance = sv_Appearance.create_appearance(... [1 1 1],[1 1 1],[],[],150,0); blue_appearance = sv_Appearance.create_appearance(... [1 1 1],[1 1 1],[],[],150,0); else red_appearance = sv_Appearance.create_appearance(... [1 0 0],[1 0 0],[],[],75,0.25); green_appearance = sv_Appearance.create_appearance(... [0 1 0],[0 1 0],[],[],75,0.25); blue_appearance = sv_Appearance.create_appearance(... [0 0 1],[0 0 1],[],[],75,0.25); end % Change the color of the primitive Cones p_cones = obj.primitives(5:7); % origin_sphere,x_cyl,y_cyl,z_cyl,x_cone,y_cone,z_cone % Cones p_cones{1}.setAppearance(red_appearance); p_cones{2}.setAppearance(green_appearance); p_cones{3}.setAppearance(blue_appearance); end
end
methods ( Static = true )
LOADOBJ Method
function B = loadobj(A) % This appears to be what happens when an object is loaded from a % file: % 1. A default object for the corresponding class is created. % This involves running the set method for all properties that % have a default value. % 2. Run the set method for all stored properties. This includes % properties that do not have defaults and properties who defaults % have been overwritten. % 3. Execute loadobj() by passing in the structure that was read % from the file. % 4. If the class description matches the output structure from % loadobj, instantiate it. % We use loadobj so that when a stored object is loaded, we can % perform necessary steps that are contained in the constructor. We % have to do this because the constructor is not executed when an % object is loaded. Log.msg(sprintf('\n%s::%s',mfilename,'loadobj (Entering)')); prop_list = eval([mfilename '.PROPERTY_LIST']); % Construct object using the stored properties storedFields = fieldnames(A); args = cell(0); for ii=1:length(storedFields) %#ok<CPROP> prop = storedFields{ii}; idx = strmatch(lower(prop),lower(prop_list),'exact'); if ~isempty(idx) args{end+1} = prop; %#ok<AGROW> args{end+1} = A.(storedFields{ii}); %#ok<AGROW> end end B = Triad(args{:}); Log.msg(sprintf('%s::%s',mfilename,'loadobj (Leaving)')); end
end methods ( Access = 'private' )
Private HELPER Methods
function add_BranchGroup(obj) Log.msg(sprintf('\n%s::%s',mfilename,'add_BranchGroup (Entering)')); obj.BranchGroup = javax.media.j3d.BranchGroup; obj.BranchGroup.setName(obj.name); obj.BranchGroup.setCapability(javax.media.j3d.BranchGroup.ALLOW_DETACH); obj.BranchGroup.setCapability(javax.media.j3d.BranchGroup.ALLOW_CHILDREN_EXTEND); end function add_TransformGroup(obj) Log.msg(sprintf('\n%s::%s',mfilename,'add_TransformGroup (Entering)')); obj.TransformGroup = javax.media.j3d.TransformGroup(); obj.TransformGroup.setName(obj.name); obj.TransformGroup.setCapability(javax.media.j3d.TransformGroup.ALLOW_TRANSFORM_READ); obj.TransformGroup.setCapability(javax.media.j3d.TransformGroup.ALLOW_TRANSFORM_WRITE); obj.TransformGroup.setCapability(javax.media.j3d.TransformGroup.ALLOW_CHILDREN_READ); obj.TransformGroup.setCapability(javax.media.j3d.TransformGroup.ALLOW_CHILDREN_WRITE); obj.TransformGroup.setCapability(javax.media.j3d.TransformGroup.ALLOW_CHILDREN_EXTEND); obj.BranchGroup.addChild(obj.TransformGroup); obj.TG_id_str = obj.TransformGroup.toString.toCharArray'; end function add_primitives(obj) % Draw with three color-coded cylinders and cones red_appearance = sv_Appearance.create_appearance(... [1 0 0],[1 0 0],[],[],75,0.25); green_appearance = sv_Appearance.create_appearance(... [0 1 0],[0 1 0],[],[],75,0.25); blue_appearance = sv_Appearance.create_appearance(... [0 0 1],[0 0 1],[],[],75,0.25); if obj.is_origin_visible sphere_appearance = sv_Appearance.create_appearance(... [0 1 1],[0 1 1],[],[],75,0.25); else sphere_appearance = sv_Appearance.create_appearance(... [0 1 1],[0 1 1],[],[],75,1); end % Cylinders x_cyl = org.j3d.renderer.java3d.geom.Cylinder(... 1, 1, red_appearance); y_cyl = org.j3d.renderer.java3d.geom.Cylinder(... 1, 1, green_appearance); z_cyl = org.j3d.renderer.java3d.geom.Cylinder(... 1, 1, blue_appearance); % Cones x_cone = org.j3d.renderer.java3d.geom.Cone(... 1, 1, red_appearance); y_cone = org.j3d.renderer.java3d.geom.Cone(... 1, 1, green_appearance); z_cone = org.j3d.renderer.java3d.geom.Cone(... 1, 1, blue_appearance); x_cone.setCapability(x_cone.ALLOW_APPEARANCE_READ); x_cone.setCapability(x_cone.ALLOW_APPEARANCE_WRITE); y_cone.setCapability(y_cone.ALLOW_APPEARANCE_READ); y_cone.setCapability(y_cone.ALLOW_APPEARANCE_WRITE); z_cone.setCapability(z_cone.ALLOW_APPEARANCE_READ); z_cone.setCapability(z_cone.ALLOW_APPEARANCE_WRITE); % Sphere origin_sphere = org.j3d.renderer.java3d.geom.Sphere(... 0.025, sphere_appearance); origin_sphere.setName('Triad origin_Sphere'); % =========================================== % Assemble Triad % Create TransformGroups for each Axis obj.axis_x_TG = javax.media.j3d.TransformGroup(); obj.axis_y_TG = javax.media.j3d.TransformGroup(); obj.axis_z_TG = javax.media.j3d.TransformGroup(); obj.axis_x_TG.setName('Triad axis_x_TG'); obj.axis_y_TG.setName('Triad axis_y_TG'); obj.axis_z_TG.setName('Triad axis_z_TG'); obj.axis_x_TG.setCapability(obj.axis_x_TG.ALLOW_TRANSFORM_WRITE); obj.axis_y_TG.setCapability(obj.axis_y_TG.ALLOW_TRANSFORM_WRITE); obj.axis_z_TG.setCapability(obj.axis_z_TG.ALLOW_TRANSFORM_WRITE); % Align and Offset TransformGroups for each Axis obj.axis_x_T3D = javax.media.j3d.Transform3D(); obj.axis_y_T3D = javax.media.j3d.Transform3D(); obj.axis_z_T3D = javax.media.j3d.Transform3D(); % Offset each axis away from origin along its direction obj.axis_T3D = javax.media.j3d.Transform3D(); obj.axis_offset_V3f = javax.vecmath.Vector3f(); % Create TransformGroups for each Arrow obj.arrow_x_TG = javax.media.j3d.TransformGroup(); obj.arrow_y_TG = javax.media.j3d.TransformGroup(); obj.arrow_z_TG = javax.media.j3d.TransformGroup(); obj.arrow_x_TG.setCapability(obj.arrow_x_TG.ALLOW_TRANSFORM_WRITE); obj.arrow_y_TG.setCapability(obj.arrow_y_TG.ALLOW_TRANSFORM_WRITE); obj.arrow_z_TG.setCapability(obj.arrow_z_TG.ALLOW_TRANSFORM_WRITE); % Offset each arrow away from origin along its direction obj.arrow_T3D = javax.media.j3d.Transform3D(); obj.arrow_offset_V3f = javax.vecmath.Vector3f(); % Attach Everything obj.TransformGroup.addChild(obj.axis_x_TG); obj.TransformGroup.addChild(obj.axis_y_TG); obj.TransformGroup.addChild(obj.axis_z_TG); % Add a small sphere to origin, but set invisible by default obj.TransformGroup.addChild(origin_sphere); obj.axis_x_TG.addChild(obj.arrow_x_TG); obj.axis_y_TG.addChild(obj.arrow_y_TG); obj.axis_z_TG.addChild(obj.arrow_z_TG); % Attach Cylinders obj.axis_x_TG.addChild(x_cyl); obj.axis_y_TG.addChild(y_cyl); obj.axis_z_TG.addChild(z_cyl); % Attach Cones obj.arrow_x_TG.addChild(x_cone); obj.arrow_y_TG.addChild(y_cone); obj.arrow_z_TG.addChild(z_cone); obj.primitives = {origin_sphere,x_cyl,y_cyl,z_cyl,x_cone,y_cone,z_cone}; obj.set_primitive_geometries; end function set_primitive_geometries(obj) % This method sets the geometry for the axes (cylinders), arrows % (cones) and origin marker (sphere) cyl_length = obj.length; cyl_radius = obj.axes_radius; cone_length = obj.arrow_length; cone_radius = obj.arrow_radius; sphere_radius = obj.origin_sphere_radius; the_primitives = obj.primitives; % Place primitives where they belong if obj.is_semi_length cyl_offset = cyl_length/2; else cyl_length = 2*cyl_length; cyl_offset = 0; end cone_offset = cyl_length/2 + cone_length/2; % Align each axis along its direction obj.axis_x_T3D.rotZ(-pi/2); obj.axis_y_T3D.rotX(0); obj.axis_z_T3D.rotX(pi/2); % Offset each axis away from origin along its direction obj.axis_offset_V3f.set(0, cyl_offset, 0); obj.axis_T3D.set(obj.axis_offset_V3f); obj.axis_x_T3D.mul(obj.axis_T3D); obj.axis_y_T3D.mul(obj.axis_T3D); obj.axis_z_T3D.mul(obj.axis_T3D); obj.axis_x_TG.setTransform(obj.axis_x_T3D); obj.axis_y_TG.setTransform(obj.axis_y_T3D); obj.axis_z_TG.setTransform(obj.axis_z_T3D); % Offset each arrow away from origin along its direction obj.arrow_offset_V3f.set(0, cone_offset, 0); obj.arrow_T3D.set(obj.arrow_offset_V3f); obj.arrow_x_TG.setTransform(obj.arrow_T3D); obj.arrow_y_TG.setTransform(obj.arrow_T3D); obj.arrow_z_TG.setTransform(obj.arrow_T3D); % Set primitives to their correct dimensions for ii = 1:obj.n_primitives prim = the_primitives{ii}; if isa(prim, 'org.j3d.renderer.java3d.geom.Cylinder') prim.setDimensions(cyl_length,cyl_radius); elseif isa(prim, 'org.j3d.renderer.java3d.geom.Cone') prim.setDimensions(cone_length,cone_radius); elseif isa(prim, 'org.j3d.renderer.java3d.geom.Sphere') % prim.setDimensions(sphere_radius); % ??? Java exception occurred: % java.lang.IllegalArgumentException: % GeometryStripArray: initial vertex index + valid % vertex count > vertex count % data = prim.getGeometry; % data.geometryType = GeometryData.INDEXED_TRIANGLE_STRIPS; % % data.geometryComponents = ... % GeometryData.TEXTURE_2D_DATA + GeometryData.NORMAL_DATA; % the_generator = obj.generator(); % if isa(prim,'org.j3d.renderer.java3d.geom.Cylinder') % the_generator.setDimensions(cyl_length,cyl_radius,true,true); % else % the_generator.setDimensions(cone_length,cone_radius,true); % end % % the_generator.generate(data); % format = GeometryArray.COORDINATES + GeometryArray.NORMALS; % i_geom = IndexedTriangleStripArray(... % data.vertexCount,... % format,... % data.indexesCount,... % data.stripCounts); % i_geom.setCoordinateIndices(0, data.indexes); % i_geom.setNormalIndices(0, data.indexes); % % geom = i_geom; % geom.setCoordinates(0, data.coordinates); % geom.setNormals(0, data.normals); % prim.setGeometry(geom); end end drawnow end
end
end