diff --git a/ShoulderCase/@Acromion/Acromion.m b/ShoulderCase/@Acromion/Acromion.m index 11f3ad6..a2f797b 100644 --- a/ShoulderCase/@Acromion/Acromion.m +++ b/ShoulderCase/@Acromion/Acromion.m @@ -1,211 +1,213 @@ classdef Acromion < handle %ACROMION Properties and methods associted to the acromion. % Detailed explanation goes here properties AI % Acromion Inddex (doi:10.2106/JBJS.D.03042) CSA % Critital Shoulder Angle (doi:10.1302/0301-620X.95B7.31028) PSA % Posterior Slope Angle (10.1016/S1058-2746(05)80036-9) PSL % Length of segment beteeen AA and AC AAA % Angulus Angle Angle (Angle between AA-origin and PA axis) AAL % Length of segment between origine and AA AAx % PA (x) position of AA AAy % IS (y) position of AA AAz % ML (z) position of AA ACx % PA (x) position of AC ACy % IS (y) position of AC ACz % ML (z) position of AC scapula comment end methods (Access = ?Scapula) % Only Scapula is allowed to construct a Acromion function obj = Acromion(scapula) %ACROMION Construct an instance of this class % Instance of the acromion objet set all properties to zero; obj.AI = []; obj.CSA = []; obj.PSA = []; obj.PSL = []; obj.AAA = []; obj.AAL = []; obj.AAx = []; obj.AAy = []; obj.AAz = []; obj.ACx = []; obj.ACy = []; obj.ACz = []; obj.comment = ''; obj.scapula = scapula; end end methods - function outputArg = measure(obj) - %MORPHOLOGY Performs morphology analysis of the acromion. + function outputArg = measureFirst(obj) + % Can be run after morphology() methods has been run by + % all ShoulderCase objects. + % % It caluculates acromion index (AI), critical shoulder angle % (CSA), and posterior slope (PS). The glenoid and scapula % surface are re-oriented (rotated and translated) in the % scapular coordinate system. For AI nd CSA, glenoid and % scapula points are projected in the scapular plane, which is % [1 0 0] after the re-orientation. % TODO: % Might be removed anatomy for concistency SCase = obj.scapula.shoulder.SCase; %% Get local coordinate system from parent scapula origin = obj.scapula.coordSys.origin; xAxis = obj.scapula.coordSys.PA; yAxis = obj.scapula.coordSys.IS; zAxis = obj.scapula.coordSys.ML; % Rotation matrix to align to scapula coordinate system R = [xAxis' yAxis', zAxis']; %% Scapula, glenoid, and scapula landmarks in scapula coordinate system % Scapula surface alignment is done in next section, for the % caulation of AI, because of mix between auto, manual and no % segmentation. This should be corrected when there will be two % scapula object per shoulder (manual and auto). % Glenoid surface in scapular coordinate system glenSurf = (obj.scapula.glenoid.surface.points - origin) * R; % Glenoid center in scapular coordinate system glenCenter = (obj.scapula.glenoid.center - origin) * R; % Scapula landmarks in scapular coordinate system AC = (obj.scapula.acromioClavicular - origin) * R; % Acromio-clavicular landmark AA = (obj.scapula.angulusAcromialis - origin) * R; % Angulus acromialis landmark obj.ACx = AC(1); obj.ACy = AC(2); obj.ACz = AC(3); obj.AAx = AA(1); obj.AAy = AA(2); obj.AAz = AA(3); %% Acromion Index (AI) % Adapted by AT (2018-07-18) from Valerie Mafroy Camine (2017) % AI = GA/GH, where: % GA: GlenoAcromial distance in scapula plane = distance from % AL to glenoid principal axis % GH: GlenoHumeral distance = 2*HHRadius, humeral head diameter (radius*2) % AL: most lateral point of the acromion % Get all required data, aligned in scapular plane, and % project in scapular plane. ScapPlaneNormal = [1 0 0]; % Normal of the scapular plane in the scapular coordinate system PlaneMean = [0 0 0]; % Origin of scapular system in scapular coordinate system % Project glenoid surface in scapular plane glenSurf = project2Plane(glenSurf,ScapPlaneNormal,PlaneMean,size(glenSurf,1)); % Project glenoid center in scapular plane glenCenter = project2Plane(glenCenter,ScapPlaneNormal,PlaneMean,size(glenCenter,1)); % If scapula is segmented, get AL from most lateral part of the % scapula, otherwise use acromio-clavicular landmark % Get segmentation propertiy from parent scapula segmentedScapula = obj.scapula.segmentation; segmentedScapula = strcmp(segmentedScapula,'A') || strcmp(segmentedScapula,'M'); if segmentedScapula == 1 % Rotate scapula surface to align the scapular plane with % the YZ plane, and then take the max Z (lateral) point % Transform scapula surface to scapula coordinate system scapSurf = obj.scapula.surface.points; scapSurf = (scapSurf - origin) * R; scapSurf = project2Plane(scapSurf,ScapPlaneNormal,PlaneMean,size(scapSurf,1)); % Take the most lateral point of the scapula, assuming that % this point is the most lateral point of the acromion [~,ALpositionInScapula] = max(scapSurf(:,3)); AL = scapSurf(ALpositionInScapula,:); else % No scapula points, we approximate AL with the acromioClavicular % landmark, in the scapula coordinate system AL = AC; % Acroomio-clavicalar scapula landmark AL = project2Plane(AL,ScapPlaneNormal,PlaneMean,size(AL,1)); end % Find glenoid principal axis with most superior and most inferior projected % glenoid points % AT: This method is not ideal. PCA would be better. It is also % used below by the CSA glenPrinAxis = [... glenSurf(glenSurf(:,2) == min(glenSurf(:,2)),:) ;... glenSurf(glenSurf(:,2) == max(glenSurf(:,2)),:)... ]; % Compute GA (distance from AL to glenoid principal axis) % Most inferior point of the scapula surface IG = glenPrinAxis(1, :); % Most superior point of the scapula surface SG = glenPrinAxis(2, :); GA = norm(cross(SG - IG, AL - IG))/norm(SG - IG); % GH (Humeral head diameter) % get humeral head radius from associated humerus GH = 2 * obj.scapula.shoulder.humerus.radius; % Acromion index if ~isempty(GH) obj.AI = GA/GH; end %% Critical Shoulder Angle (CSA) % Adapted by AT (2018-07-18) from Bharath Narayanan (2018) % [radCSA, degCSA] = computeCSA(GlenoidPtsinScapulaPlane, ALinScapulaPlane); % Vectors connecting IG to SG, and IG to AL IGSG = SG - IG; IGAL = AL - IG; CSA = vrrotvec(IGSG,IGAL); CSA = CSA(4); CSA = rad2deg(CSA); obj.CSA = CSA; %% Posterior Slope Angle and length (PSA & PSL) % By AT (2018-08-10) % Project AA & AC in PA-IS plane AAxy = [AA(1:2) 0]; % Juste take x and y component ACxy = [AC(1:2) 0]; % Juste take x and y component PSv = ACxy - AAxy; % Posterior slope vector ISv = [1 0 0]; % IS axis PSA = vrrotvec(PSv, ISv); PSA = PSA(4); PSA = rad2deg(PSA); PSL = norm(PSv); obj.PSA = PSA; obj.PSL = PSL; %% Acromial Angle Angle and length (AAA & AAL) % By AT (2018-09-13) % Vector between scapular origin and AA in the plane PA-IS AAv = [AA(1:2) 0]; % Angle between AAvect and PA axis PAv = [1 0 0]; % PS axis AAA = vrrotvec(AAv, PAv); AAA = AAA(4); AAA = rad2deg(AAA); AAA = 180 - AAA; AAL = norm(AAv); obj.AAA = AAA; obj.AAL = AAL; %% We might save an image of AI, CSA, PS outputArg = 1; end end end diff --git a/ShoulderCase/@Scapula/Scapula.m b/ShoulderCase/@Scapula/Scapula.m index 5c13a6d..57154af 100644 --- a/ShoulderCase/@Scapula/Scapula.m +++ b/ShoulderCase/@Scapula/Scapula.m @@ -1,53 +1,57 @@ classdef (Abstract) Scapula < handle % This class defines the scapula. Landmarks are used to define its % coordinate system. It includes the glenoid object. properties angulusInferior = []; % Landmark Angulus Inferior read from amira angulusInferiorLocal = []; % Same as above, expressed the scapular coordinate system rather than the CT coordinate system trigonumSpinae = []; % Landmark Trigonum Spinae Scapulae (the midpoint of the triangular surface on the medial border of the scapula in line with the scaoular spine read from amira processusCoracoideus = []; % Landmark Processus Coracoideus (most lateral point) read from amira acromioClavicular = []; % Landmark Acromio-clavicular joint (most lateral part on acromion)read from amira angulusAcromialis = []; % Landmark Angulus Acromialis (most laterodorsal point) read from amira spinoGlenoidNotch = []; % Landmark Spino-glenoid notch read from amira pillar = []; % 5 landmarks on the pillar groove = []; % 5 landmarks on the scapula (supraspinatus) groove friedmansLine = []; % Line that goes through trigonumSpinae end the glenoid center coordSys % Scapular coordinate system plane % Plane class segmentation = "N"; % either none 'N', manual 'M' or automatic 'A' surface = [];% surface points and triangles of the scapula glenoid % Glenoid class acromion % Acromion class comment = ""; % any comment shoulder end methods (Abstract) loadData(obj); end methods function obj = Scapula(shoulder) obj.shoulder = shoulder; obj.coordSys = CoordinateSystemAnatomical(); obj.plane = Plane(); obj.acromion = Acromion(obj); end function output = morphology(obj) + % Call methods that can be run after loadData() methods has been run by + % all ShoulderCase objects. obj.measurePlane(); obj.measureCoordinateSystem(); end - function output = measure(obj) + function output = measureFirst(obj) + % Call methods that can be run after morphology() methods has been run by + % all ShoulderCase objects. obj.measureFriedmansLine(); end end end