#!/usr/bin/env pascal
{
================================================================================
# BEGIN FILE: framer_plot_commented.pas
# Date: June 26, 2025
# Author(s): Gary Argraves, Claude (Anthropic) - commenting only
# 
# Purpose: Multi-story, multi-bay rigid frame structural analysis with 
#          interactive SVG output generation for web deployment
#
# Usage Example:
#   ./framer_plot < input.dat > output.html
#   Used as CGI backend: /cgi-bin/g/framer.cgi
#
# Features:
#   - Matrix stiffness method for rigid frame analysis
#   - Macro loading system for reusable load patterns  
#   - Interactive SVG moment/shear diagrams with mouse-over data
#   - Web-based delivery through HTML/JavaScript integration
#   - Professional engineering graphics with proper scaling
#   - Both gravity and lateral load handling
#   - Fixed or pinned base support conditions
#
# Original Algorithm: Gary Argraves 1987, evolved to web version 2008+
#
================================================================================
}

program framer;

uses sysutils;

type
  str60  = string[60];
  str30  = string[30];
  all_char = set of ' '..'}';

var
   {=== STRUCTURAL ANALYSIS MATRICES ===}
   d  : array[1..50,1..50] of real;  { Global stiffness matrix + load vector }
   b  : array[1..40,1..6]  of real;  { Beam properties: [I, L, FEM_L, FEM_R, V_L, V_R] }
   c  : array[1..40,1..2]  of real;  { Column properties: [I, L] }
   e  : array[1..10,1..4]  of real;  { Wind loading: [FEM_bot, FEM_top, V_bot, V_top] }
   
   {=== LOAD MANAGEMENT SYSTEM ===}
   ld : array[1..50,1..4]  of real;  { Load definitions: [story, intensity, start, length] }
   mp : array[1..20,1..2]  of integer; { Macro pointers: [start_load, end_load] }
   r  : array[0..400] of real;  { Tabulated moment/shear values for plotting }

   {=== FRAME GEOMETRY & ANALYSIS CONTROL ===}
   s,p   : integer;  { Number of stories, number of portals(bays) }
   q     : integer;  { Size of matrix to solve (degrees of freedom) }
   z     : integer;  { Base joint type: 1=fixed, 2=pinned }
   l2,l3 : real;     { Total frame width, total frame height (feet) }

   {=== LOAD TRACKING VARIABLES ===}
   lp    : integer;  { Load pointer: next available row in load matrix }
   mc    : integer;  { Macro counter: number of macro load patterns defined }
   lc    : integer;  { Load count: number of loads currently applied }
   lm    : array[1..50] of integer; { List of applied load indices for plotting }
   
   {=== PLOTTING & SCALING PARAMETERS ===}
   psl   : integer;  { Plot scale for member length (feet per inch) }
   psm   : integer;  { Plot scale for moments (kip-ft per inch) }
   
   {=== PROJECT DOCUMENTATION ===}
   proj_descrip : str60;  { Project name/description }
   load_descrip : str60;  { Current load case description }
   time         : str30;  { Timestamp for analysis }
   option       : integer; { User menu choice }
   shear        : boolean; { Flag: true=plot shear, false=plot moment }

const 
    {=== SVG GRAPHICS SCALING ===}
    puf = 96; { Plotter unit factor: 96 dots per inch for web display
              Original code used 254 for Epson plotter, 96 for screen display
              Modern options: 300/600/2400 for laser printer output }

var
	{=== PAGE LAYOUT PARAMETERS ===}
	page_width :integer = 1800;   { SVG canvas width in pixels }
	page_height :integer= 1100;   { SVG canvas height in pixels }

	{=== GRAPHICS STATE VARIABLES ===}
	cur_x:integer;                 { Current X position for drawing }
	cur_y:integer;                 { Current Y position for drawing }
	char_height:integer;           { Text character height }
	char_width:integer;            { Text character width }
	char_draw_angle:integer;       { Text rotation angle }
	draw_pen:integer;              { Current pen/color selection }
	
	{=== SVG OUTPUT GENERATION ===}
	plot_str:AnsiString;           { Accumulates SVG drawing commands }
	
	{=== INTERACTIVE DATA CORRELATION ===}
	gX,gY:real;                    { Current coordinate for mouse-over display }
	gWhat:string;                  { Member identification for mouse-over }
	mouseover_enabled:boolean=false; { Control flag for interactive elements }
	tabulation_str:AnsiString;     { Accumulates data table for mouse-over }
	tab_cnt:integer=0;             { Counter for unique element IDs }

{=== GRAPHICS PRIMITIVES ===}

procedure MA(x:integer; y:integer);
{ Move Absolute: Set current drawing position without drawing }
	begin
		cur_x:=x;
		cur_y:=y;
	end;

procedure DA(x:integer; y:integer);
{ Draw Absolute: Draw line from current position to (x,y) and update position }
	var color:string = 'black';
		thickness:integer =1;

	begin
		{=== PEN COLOR/THICKNESS MAPPING ===}
		case draw_pen of
			1: begin color:='black'; thickness:=1; end;  { Structure outline }
			2: begin color:='red';  thickness:=1; end;   { Shear diagrams }
			3: begin color:='blue'; thickness:=2; end;   { Moment diagrams }
		end;
		
		{=== COORDINATE SYSTEM TRANSFORMATION ===}
		{ SVG uses top-left origin, structural engineering uses bottom-left
		  Transform: Y_svg = page_height - Y_structural }

		{=== INTERACTIVE ELEMENT GENERATION ===}
		{ Create invisible circles for mouse-over coordinate capture }
		if mouseover_enabled then
		begin
			tab_cnt:=tab_cnt+1;
			
			{ Invisible circle with mouse-over event handler }
			appendStr( plot_str, format('<circle cx="%d" cy="%d" r="5" fill="transparent" fill-opacity="0" onmouseover="showTab(%d);" />',
											[x, page_height - y, tab_cnt]) );

			{ Corresponding data entry for tabulation panel }
			appendStr (tabulation_str, format('<span id="tab%d">%3.1f  %7.3f</span><br>', [tab_cnt, gX, gY]) );
		end;

		{=== SVG LINE ELEMENT GENERATION ===}
		appendStr ( plot_str, format('<line x1="%d" y1="%d" x2="%d" y2="%d"  style="stroke:%s; stroke-width:%dpx;"/> ',
										[cur_x, page_height - cur_y,  { Transform Y coordinates }
										     x, page_height - y, 
											 color, thickness ]) ) ; 
		cur_x:=x;
		cur_y:=y;
	end;

procedure LA( text:string);
{ Label Absolute: Place text at current position }
	begin
		appendStr( plot_str, format('<text x="%d" y="%d">%s</text>', [cur_x, page_height-cur_y, text]) );
	end;

procedure SI(h:integer; w:integer);
{ Set character height and width for text output }
	begin
		char_height:=h;
		char_width:=w;
	end;

procedure DI(a:integer);
{ Set character drawing angle (text rotation) }
	begin
		char_draw_angle:=a;
	end;

procedure SP(pen:integer);
{ Select Pen: Set current drawing color/style }
	begin
		draw_pen:=pen;
	end;

{=== UTILITY FUNCTIONS ===}

function  when:string;
{ Return current date/time as formatted string }
  begin
	when:=DateTimeToStr(Now)
  End;

function parse_up(s:str60):str60;
{ Convert string to uppercase for consistent processing }
  var i: integer;
  begin
    for i:=1 to length(s) do s[i]:=upcase(s[i]);
    parse_up:=s;
  end;

function pw(n:real; p:integer):real;
{ Power function: calculate n^p (used in load distribution integrals) }
  var i:integer;
      a:real;
  begin
    a:=1;
    for i:=1 to p do a:=a*n;
    pw:=a;
  end;

{=== INPUT PARSING FUNCTIONS ===}

procedure readr( var r:real);
{ Read real number with error handling and retry capability }
  var
    i : integer;
  begin
    repeat
      {$I-} read(r); {$I+} i:=IOResult;
      if I<>0 then readln;  { Skip line on error and retry }
    until i=0;
  end;

procedure readi( var r:integer);
{ Read integer with error handling and retry capability }
  var
    i : integer;
  begin
    repeat
      {$I-} read(r); {$I+} i:=IOResult;
      if I<>0 then readln;  { Skip line on error and retry }
    until i=0;
  end;

procedure reads(var s:str60);
{ Read string (no error handling needed) }
  begin
    read(s);
  end;

{=== PROGRAM INITIALIZATION ===}

procedure signon;
{ Display program header and get project information }
  begin
    writeln('RIGID FRAME ANALYSIS  By Gary Argraves - 8701.15');
    writeln;
    write('Project Designation ? '); reads(proj_descrip);
    writeln(proj_descrip);
    writeln('  Date: ',time);
    writeln;
  end;

 procedure fatal_err(s:str60);
 { Handle fatal errors with message and program termination }
   begin
     writeln(s);
     halt;
   end;

procedure initial_var;
{ Initialize all arrays and variables to zero/default states }
  var i,j:integer;
  begin
    lp:=1;    { Start load pointer at 1 }
    mc:=0;    { No macros defined yet }
    lc:=0;    { No loads applied yet }
    
    {=== ZERO ALL ANALYSIS MATRICES ===}
    for i:=1 to 50 do for j:=1 to 50 do d[i,j]:=0;  { Global stiffness matrix }
    for i:=1 to 40 do for j:=1 to 6 do b[i,j]:=0;   { Beam data }
    for i:=1 to 40 do for j:=1 to 2 do c[i,j]:=0;   { Column data }
    for i:=1 to 10 do for j:=1 to 4 do e[i,j]:=0;   { Wind loading }
    for i:=1 to 50 do for j:=1 to 4 do ld[i,j]:=0;  { Load definitions }
    for i:=1 to 20 do for j:=1 to 2 do mp[i,j]:=0;  { Macro pointers }
    for i:=1 to 50 do lm[i]:=0;                      { Applied load list }
  end;

{=== STRUCTURAL INPUT FUNCTIONS ===}

procedure get_size;
{ Input frame dimensions: number of stories and bays }
  begin
     writeln('___INPUT FRAME SIZE___');
     write('How many Stories ? ':25); readi(s);  writeln(s:6);
     write('How many Portals ? ':25); readi(p);  writeln(p:6);
     writeln;
     q:=(s+1)*(p+1)+s;  { Calculate degrees of freedom for matrix solution }
  end;

procedure get_geometry;
{ Input frame geometry: story heights and bay widths }
  var
   i,j,z2  : integer;
   l       : real;
  begin
    writeln('___INPUT FRAME GEOMETRY (units=feet)___');
    
    {=== STORY HEIGHTS INPUT ===}
    writeln('   Input story heights.');
    l3:=0;  { Total height accumulator }
    for i:=1 to s do
      begin
        write('Story #':12,i:2,' ? '); readr(l); writeln(l:8:2);
        l3:=l3+l;  { Accumulate total height }
        
        { Assign height to all columns in this story }
        z2:=(p+1)*(s-i)+1;  { Starting column index for this story }
        for j:=z2 to z2+p do c[j,2]:=l;
      end;
    writeln('  Total height=',l3:7:2);
    writeln;
    
    {=== BAY WIDTHS INPUT ===}
    writeln('   Input beam lengths.');
    l2:=0;  { Total width accumulator }
    for i:=1 to p do
      begin
        write('Beam #':12,i:2,' ? '); readr(l); writeln(l:8:2);
        l2:=l2+l;  { Accumulate total width }
        
        { Assign length to all beams in this bay across all stories }
        j:=i; 
        repeat 
          b[j,2]:=l; 
          j:=j+p;  { Jump to same bay in next story }
        until j>i+(s-1)*p;
      end;
    writeln('  Total length=',l2:7:2);
    
    {=== AUTOMATIC SCALE CALCULATION ===}
    psl:=5;   { Start with 5 feet per inch }
    psm:=50;  { Start with 50 kip-ft per inch }
    while l2/psl>9 do  { Keep diagram under 9 inches wide }
      begin 
        psl:=psl+10;   { Increase length scale }
        psm:=psm+100;  { Proportionally increase moment scale }
      end;

    {=== PLOTTING PARAMETERS INPUT ===}
    writeln;
    write('Page width (pixels) =',page_width:5,' ? '); readi(page_width);
	writeln;
    write('Page height (pixels) =',page_height:5,' ? '); readi(page_height);
	writeln;
    write('Plot Scale Length (ft/in) =',psl:3,' ? '); readi(psl);
	writeln;
    write('Plot Scale Moment (kip-ft/inch) =',psm:4,' ? '); readi(psm);
	writeln;
	writeln;
  end;

procedure get_member_sizes;
{ Input member properties: moment of inertia for beams and columns by story }
  var
    i,j:integer;
  begin
    writeln('___INPUT MEMBER SIZES - Moment of Inertia (I) (units=inch^4)___');
    
    {=== INPUT BY STORY (TOP TO BOTTOM) ===}
    for i:=1 to s do
      begin
        writeln;
        writeln('STORY #':12,i:2);
        writeln('-------':12,'--');
        
        {=== BEAM SIZES FOR THIS STORY ===}
        for j:=1 to p do
          begin
            write('Beam #':12,j:2,' ? ');
            readr(b[j+p*(s-i),1]); writeln(b[j+p*(s-i),1]:10:2);
          end;
          
        {=== COLUMN SIZES FOR THIS STORY ===}
        for j:=1 to p+1 do
          begin
            write('Column #':12,j:2,' ? ');
            readr(c[j+(s-i)*(p+1),1]); writeln(c[j+(s-i)*(p+1),1]:10:2);
          end;
      end;
    writeln;
  end;

procedure get_base_joint_type;
{ Select base connection type: fixed or pinned }
  begin
    writeln('___BASE JOINT TYPE___');
    writeln('1. Fixed ':15);
    writeln('2. Pinned':15);
    repeat
      write('Enter 1 or 2 ? ':12); readi(z); writeln(z:3);;
    until z in [1,2];
    writeln;
  end;

{=== LOAD DEFINITION SYSTEM ===}

procedure get_macro_loads;
{ Define reusable load patterns (macros) with flexible positioning }
  var
    i     : integer;
    w,j,k : real;    { Load intensity, start position, length }
    l4    : real;    { Reference length for load validation }
  begin
    writeln('  ___________ MACRO LOAD DEFINITION ____________');
    writeln;
    writeln('            +-A--+---C---+');
    writeln('         W  !    #########--W');
    writeln('          \ ====================== -- Story 2');
    writeln('        -+-#I      I      I      I');
    writeln('         | #I      I      I      I');
    writeln('      C--| #I      I      I      I');
    writeln('         | #====================== -- Story 1');
    writeln('        -+-#I      I      I      I');
    writeln('      A--|  I  P1  I  P2  I  P3  I');
    writeln('       __|__I______I______I______I___ Ground');
    writeln;
    writeln('  S=0 :Lateral Load   S=-1 :Next Macro Definition');
    writeln('  S>0 :Gravity Load   S=-2 :Quit Macro Definition');
    writeln;

    {=== MACRO DEFINITION LOOP ===}
    repeat
      mc:=mc+1;          { Increment macro counter }
      mp[mc,1]:=lp;      { Mark start of this macro's loads }
      
      {=== LOAD INPUT LOOP FOR CURRENT MACRO ===}
      repeat
        writeln('Macro #':12,mc:2,'  Load #',lp:2);
        writeln('-------':12,'--','  ------','--');
        write('Enter Story # S=? ':22); readi(i); writeln(i:5);
        
        { Validate story number }
        if (i>s) then fatal_err('*** Error: Incorrect stories number ***');
        
        { Set reference length: height for lateral loads, width for gravity }
        if i=0 then l4:=l3 else l4:=l2;
        
        if i>=0 then
          begin
             {=== LOAD PARAMETERS INPUT ===}
             write('Intensity W=? ':22); readr(w);  writeln(w:8:2);  { Load per unit length }
             write('Location  A=? ':22); readr(j);  writeln(j:8:2);  { Start position }
             write('Length    C=? ':22); readr(k);  writeln(k:8:2);  { Load length }
             
             { Validate load doesn't exceed frame dimensions }
             if j+k>l4 then fatal_err('*** Error: A+C> Total Length of Frame ***');
             
            {=== STORE LOAD DEFINITION ===}
            ld[lp,1]:=i;  { Story number (0=lateral) }
            ld[lp,2]:=w;  { Load intensity }
            ld[lp,3]:=j;  { Start position }
            ld[lp,4]:=k;  { Load length }
            mp[mc,2]:=lp; { Mark end of this macro's loads }
            lp:=lp+1;    { Advance load pointer }
          end
        else if mp[mc,2]=0 then mc:=mc-1;  { Cancel empty macro }
        writeln;
      until i<0;  { -1 = next macro, -2 = quit }
    until i<-1;
  end;

{=== LOAD APPLICATION AND PROCESSING ===}

procedure apply_macro_loads;
{ Apply selected macro loads and convert to fixed-end moments and shears }
  var
    u,i,m : integer;
    w,j,k,t,t2 : real;

  {=== LATERAL LOAD PROCESSING ===}
  procedure process_lateral_load;
  { Convert distributed lateral loads to fixed-end moments and shears }
    var
      h,o   : integer;
      a,b2,c2,l : real;

    begin
     {=== PROCESS EACH STORY FOR LATERAL LOADS ===}
     for H:=1 TO S do
       begin
         O:=1+(S-H)*(P+1);                    { Column index for this story }
         T:=T+C[O,2];                         { Height to top of this column }
         T2:=T-C[O,2];                        { Height to bottom of this column }
         
         {=== CHECK IF LOAD INTERSECTS THIS COLUMN SEGMENT ===}
         IF (J<T) and (J+K>T2) THEN
           begin
             A:=0;                            { Relative start position in segment }
             IF J>T2 THEN A:=J-T2;            { Load starts partway up segment }
             B2:=C[O,2]-A;                    { Load length in this segment }
             IF J+K<T THEN B2:=J+K-A-T2;      { Load doesn't reach end of segment }
             L:=C[O,2];                       { Column length }
             C2:=A+B2;                        { End position of load in segment }
             
             {=== CALCULATE FIXED-END MOMENTS (INTEGRATION OF LOAD POLYNOMIALS) ===}
             { Bottom FEM: negative of integral expression }
             E[H,1]:=E[H,1]-(W*ABS(1/12/sqr(L)*(6*sqr(L)*(sqr(C2)-sqr(A))-8*L*(pw(C2,3)-pw(A,3))+3*(pw(C2,4)-pw(A,4)))));
             { Top FEM: positive of different integral expression }
             E[H,2]:=E[H,2]+(W*ABS(1/12/sqr(L)*(4*L*(pw(C2,3)-pw(A,3))-3*(pw(C2,4)-pw(A,4)))));
             
             {=== CALCULATE SHEAR FORCES ===}
             E[H,3]:=E[H,3]+(W*B2*(A+B2/2));     { Bottom shear }
             E[H,4]:=E[H,4]+(W*B2*(L-A-B2/2));   { Top shear }
           end;
       end;
    end;

  {=== GRAVITY LOAD PROCESSING ===}
  procedure process_beam_load;
  { Convert distributed gravity loads to fixed-end moments and shears }
    var
      h,o   : integer;
      a,b2,c2,l : real;
    begin
      {=== PROCESS EACH BAY FOR GRAVITY LOADS ===}
      FOR H:=1 TO P do
        begin
          O:=H+(S-I)*P;                       { Beam index for this bay/story }
          T:=T+B[O,2];                        { Position to right end of beam }
          T2:=T-B[O,2];                       { Position to left end of beam }
          
          {=== CHECK IF LOAD INTERSECTS THIS BEAM SEGMENT ===}
          IF (J<T) and (J+K>T2) THEN
            begin
              A:=0; IF J>T2 THEN A:=J-T2;     { Relative start position }
              B2:=B[O,2]-A; IF J+K<T THEN B2:=J+K-A-T2; { Load length in segment }
              L:=B[O,2];                      { Beam length }
              C2:=A+B2;                       { End position of load }
              
              {=== CALCULATE FIXED-END MOMENTS ===}
              { Left FEM: negative (sagging moment convention) }
              B[O,3]:=B[O,3]-(W*ABS(1/12/sqr(L)*(6*sqr(L)*(sqr(C2)-sqr(A))-8*L*(pw(C2,3)-pw(A,3))+3*(pw(C2,4)-pw(A,4)))));
              { Right FEM: positive }
              B[O,4]:=B[O,4]+(W*ABS(1/12/sqr(L)*(4*L*(pw(C2,3)-pw(A,3))-3*(pw(C2,4)-pw(A,4)))));
              
              {=== CALCULATE SHEAR FORCES ===}
              B[O,5]:=B[O,5]+(W*B2*(A+B2/2));     { Left shear }
              B[O,6]:=B[O,6]+(W*B2*(L-A-B2/2));   { Right shear }
            end;
        end;
    end;

  {=== MAIN LOAD APPLICATION PROCEDURE ===}
  begin
     writeln('___APPLY LOADS TO FRAME___');
     writeln;
     writeln('  # > 0 :Macro Loading');
     writeln('  # < 0 :Single Load');
     writeln('  # = 0 :Quit loading');
     writeln;
     lc:=0;  { Reset load counter }
     
     {=== GET LOAD CASE DESCRIPTION ===}
     write('Enter Loading Designation ? ');
     readln;  { Clear input buffer }
     reads(load_descrip); load_descrip:=parse_up(load_descrip);
     writeln(load_descrip);
     
     {=== LOAD APPLICATION LOOP ===}
     repeat
       write('Enter # ? ');  readi(m); writeln(m:3);
       
       if (m>0) and (m<=mc) then begin
           {=== APPLY COMPLETE MACRO ===}
           for u:=mp[m,1] to mp[m,2] do
             begin
               lc:=lc+1; lm[lc]:=u;  { Track applied loads for plotting }
               i:=trunc(ld[u,1]); w:=ld[u,2]; j:=ld[u,3]; k:=ld[u,4]; 
               t:=0; t2:=0;  { Reset position trackers }
               
               { Process based on load type }
               if i=0 then process_lateral_load else process_beam_load;
             end;
         end
       else if (m<0) and (m>-lp) then
              {=== APPLY SINGLE LOAD ===}
              begin
                u:=abs(m);
                lc:=lc+1; lm[lc]:=u;
                i:=trunc(ld[u,1]); w:=ld[u,2]; j:=ld[u,3]; k:=ld[u,4]; 
                t:=0; t2:=0;
                if i=0 then process_lateral_load else process_beam_load;
              end
            else if m<>0 then fatal_err('*** Error: No such Macro or Load. ***');
     until m=0;
   writeln;
  end;

{=== MATRIX STRUCTURAL ANALYSIS ===}

procedure setup_matrix;
{ Assemble global stiffness matrix using slope-deflection method }
  var
    z2,u,i,j : integer;
    o : real;
  label
    NEXT_J;
  begin
    {=== DETERMINE MATRIX SIZE BASED ON BASE CONDITIONS ===}
    Z2:=S+1;
    IF Z=1 THEN Z2:=S;  { Fixed base: reduce matrix size }
    
    {=== ASSEMBLE STIFFNESS MATRIX BY STORY LEVELS ===}
    for U:=1 TO Z2 do
      for J:=S+U*(1+P)-P TO S+U*(1+P) do  { Joint range for this story }
        begin
          {=== COLUMN BELOW JOINT ===}
          IF U<>1 then begin
            O:=C[J-S-P-1,1]/C[J-S-P-1,2];    { Column stiffness factor }
            IF J=S+U*(1+P)-P THEN D[J,Q+1]:=E[S+2-U,1]; { Add wind FEM }
            
            {=== ADD COLUMN STIFFNESS CONTRIBUTIONS ===}
            D[J,J]:=4*O+D[J,J];               { Joint rotation stiffness }
            D[J,J-P-1]:=2*O;                  { Carryover to adjacent joint }
            O:=O/C[J-S-P-1,2];               { Scale for displacement coupling }
            D[J,U-1]:=6*O+D[J,U-1];          { Joint-displacement coupling }
            
            IF U=S+1 THEN GOTO NEXT_J;       { Skip if at top level }
            
            {=== DISPLACEMENT-ROTATION COUPLING ===}
            D[U,J]:=-6*O;
            D[U,J-P-1]:=-6*O;
            D[U,U]:=D[U,U]+12*O/C[J-S-P-1,2];
            D[U,U-1]:=D[U,U-1]-12*O/C[J-S-P-1,2];
            D[J,U]:=D[J,U]-6*O;
          end;
          
          {=== COLUMN ABOVE JOINT ===}
          O:=C[J-S,1]/C[J-S,2];              { Column stiffness factor }
          IF J=S+U*(1+P)-P THEN D[J,Q+1]:=D[J,Q+1]+E[S+1-U,2]; { Add wind FEM }
          D[J,J]:=D[J,J]+4*O;                { Add to joint stiffness }
          
          IF (Z<>1) or (u<>s) THEN begin     { If not fixed base at top story }
            D[U,J+P+1]:=6*O/C[J-S,2];
            D[J,J+P+1]:=2*O;
          end;
          
          {=== DISPLACEMENT COUPLING ===}
          O:=O/C[J-S,2];
          D[U,J]:=D[U,J]+6*O;
          D[J,U]:=D[J,U]+6*O;
          D[U,U]:=D[U,U]+12*O/C[J-S,2];
          
          IF U<>S THEN begin
            D[J,U+1]:=D[J,U+1]-6*O;
            D[U,U+1]:=D[U,U+1]-12*O/C[J-S,2];
          end;
          
          {=== BEAM TO RIGHT OF JOINT ===}
          IF J<>S+U*(1+P) THEN begin
            O:=B[J-S-U+1,1]/B[J-S-U+1,2];    { Beam stiffness factor }
            D[J,J+1]:=2*O;                    { Carryover stiffness }
            D[J,J]:=D[J,J]+4*O;               { Add to joint stiffness }
            D[J,Q+1]:=D[J,Q+1]+B[J-S-U+1,3]; { Add fixed-end moment }
          end;
          
          {=== BEAM TO LEFT OF JOINT ===}
          IF J<>S+U*(1+P)-P THEN begin
            O:=B[J-S-U,1]/B[J-S-U,2];        { Beam stiffness factor }
            D[J,J]:=D[J,J]+4*O;               { Add to joint stiffness }
            D[J,J-1]:=D[J,J-1]+2*O;           { Carryover stiffness }
            D[J,Q+1]:=D[J,Q+1]+B[J-S-U,4];   { Add fixed-end moment }
          end;
         NEXT_J:
        end{for j};

     {=== ASSEMBLE DISPLACEMENT EQUILIBRIUM EQUATIONS ===}
     FOR I:=1 TO S do begin
       U:=S+1-I;  { Story index from bottom }
       { Sum of shears must equal applied lateral load }
       D[I,Q+1]:=(E[U,3]+E[U,2]+E[U,1])/C[(I-1)*(P+1)+1,2];
       IF I>1 THEN D[I,Q+1]:=D[I,Q+1]+(E[U+1,4]-E[U+1,2]-E[U+1,1])/C[(I-2)*(P+1)+1,2];
     end;
  end;

procedure solve_matrix;
{ Solve system of linear equations using Gaussian elimination with pivoting }
  var
    i,j,l : integer;
    m : real;
    matrix_err,go,restart : boolean;

begin
  matrix_err:=false;
  
  {=== GAUSSIAN ELIMINATION WITH PARTIAL PIVOTING ===}
  repeat
    restart:=false;
    i:=1;
    repeat
      {=== SPECIAL HANDLING FOR FIXED BASE CONDITIONS ===}
      IF (Z=1) and (I=Q-P) THEN exit;
      
      {=== CHECK FOR ZERO PIVOT ===}
      IF D[I,I]=0 THEN
        begin
          IF I=Q THEN matrix_err:=true;  { Singular matrix }
          
          if not matrix_err then
            begin
              {=== SEARCH FOR NON-ZERO ELEMENT TO SWAP ROWS ===}
              j:=q; go:=false; restart:=false;
              repeat
                IF (J<>I) and (D[J,I]<>0) then
                  begin
                    {=== SWAP ROWS ===}
                    FOR L:=1 TO Q+1 do D[I,L]:=D[I,L]+D[J,L];
                    if j>i then go:=true
                    else begin restart:=true; i:=q; end;
                  end;
                j:=j-1;
                if (not go) and (not restart) and (j=0) then matrix_err:=true;
              until (j=0) or (go) or (restart);
            end;
          if matrix_err then fatal_err('*** Matrix solution Error ***');
        end;
        
      if not restart then
        begin
          {=== NORMALIZE PIVOT ROW ===}
          M:=D[I,I];
          FOR L:=1 TO Q+1 do D[I,L]:=D[I,L]/M;
          
          {=== ELIMINATE COLUMN BELOW AND ABOVE PIVOT ===}
            FOR J:=1 TO Q do
              IF J<>I THEN
                begin 
                  M:=D[J,I];
                  FOR L:=1 TO Q+1 do D[J,L]:=D[J,L]-M*D[I,L];
                end;
        end;
      i:=i+1;
    until i>q;
  until not restart;
  end;

{=== RESULT PROCESSING AND PLOTTING ===}

procedure plot_beam_mom(story,beam:integer; m1,v1:real);
{ Generate SVG for beam moment/shear diagram with interactive data points }
  var
    w,a,c2,l: real;     { Load parameters and beam length }
    sb,eb   : real;     { Start/end beam positions in frame }
    sh      : real;     { Story height position }
    last_lc : integer;  { Load counter for processing }
    i,k     : integer;  { Loop indices }
    ox,oy   : integer;  { Origin for this beam diagram }
    x       : real;     { Distance along beam }

  {=== FIND LOADS AFFECTING THIS BEAM ===}
  procedure find_load(var w,a,c:real);
    var
      sl,el : real; { Start/end positions of load }
    begin
      w:=0;  { Initialize: no load found }
      while (last_lc<=lc) and (w=0) do
        begin
          if ld[lm[last_lc],1]=story then  { Load applies to this story }
            begin
              sl:=ld[lm[last_lc],3];      { Load start position }
              el:=sl+ld[lm[last_lc],4];   { Load end position }
              
              {=== CHECK IF LOAD INTERSECTS THIS BEAM ===}
              if (sl<sb) and (el>sb) then
                begin 
                  w:=ld[lm[last_lc],2]; a:=0; c:=el-sb; 
                end
              else if (sl>=sb) and (sl<eb) then
                     begin 
                       w:=ld[lm[last_lc],2]; a:=sl-sb; c:=el-sl; 
                     end;
              if (w<>0) and (a+c>l) then c:=l-a;  { Truncate if exceeds beam }
            end;
          last_lc:=last_lc+1;
        end;
    end;

  {=== CALCULATE MOMENT CONTRIBUTIONS FROM DISTRIBUTED LOADS ===}
  procedure tab_moments;
    var
      x,mx : real;
    begin
      x:=0.5; k:=1;  { Start at 0.5 ft, increment by 0.5 ft }
      repeat
        mx:=0;
        {=== PARABOLIC MOMENT FROM DISTRIBUTED LOAD ===}
        if (x>a) and (x<=a+c2) then  
          mx:=w/2*sqr(x-a)                    { Parabolic portion }
        else if x>a+c2 then 
          mx:=c2*w*(x-(a+c2/2));             { Linear portion beyond load }
        r[k]:=r[k]+mx;  { Add to existing moment }
        x:=x+0.5; k:=k+1;
      until x>=l;
      r[k]:=r[k]+c2*w*(l-(a+c2/2));  { Final moment at beam end }
    end;

  {=== CALCULATE SHEAR CONTRIBUTIONS FROM DISTRIBUTED LOADS ===}
  procedure tab_shears;
    var
      x,vx : real;
    begin
      x:=0.5; k:=1;
      repeat
        vx:=0;
        {=== LINEAR SHEAR FROM DISTRIBUTED LOAD ===}
        if (x>a) and (x<=a+c2) then  
          vx:=(x-a)*w                        { Linear increase in load zone }
        else if x>a+c2 then 
          vx:=c2*w;                          { Constant beyond load }
        r[k]:=r[k]-vx;  { Subtract from existing shear (sign convention) }
        x:=x+0.5; k:=k+1;
      until x>=l;
      r[k]:=r[k]-c2*w;  { Final shear at beam end }
    end;

  {=== MAIN BEAM PLOTTING PROCEDURE ===}
  begin
    {=== CALCULATE BEAM POSITION IN FRAME ===}
    sb:=0; 
    for i:=1 to beam-1 do sb:=sb+b[p*(s-story)+i,2];  { Start position }
    eb:=sb+b[p*(s-story)+beam,2];                      { End position }
    sh:=0; 
    for i:=1 to story do sh:=sh+c[(p+1)*(s-i)+1,2];   { Story height }
    l:=eb-sb;  { Beam length }
    
    {=== INITIALIZE TABULATION ARRAY ===}
    last_lc:=1;
    for i:=0 to 200 do r[i]:=0;
    
    {=== CALCULATE BASE MOMENT/SHEAR FROM BEAM END FORCES ===}
    i:=0; x:=0;
    repeat
      if shear then 
        r[i]:=v1  { Constant shear from end forces }
      else 
        r[i]:=m1-v1*x;  { Linear moment from end forces }
      i:=i+1; x:=x+0.5;
    until x>=l; 
    
    if shear then 
      r[i]:=v1 
    else 
      r[i]:=m1-v1*l;
    
    {=== ADD CONTRIBUTIONS FROM DISTRIBUTED LOADS ===}
    repeat
      find_load(w,a,c2);
      if w<>0 then begin 
        if shear then tab_shears else tab_moments; 
      end;
    until last_lc>lc;
    
    k:=i;  { Save final index }
    
    {=== CALCULATE PLOTTING ORIGIN ===}
    ox:=round( ( sb/psl + 0.20*(beam)+0.50) *puf);
    oy:=round( ( sh/psl + 0.20*(story)    ) *puf);

	{=== DRAW BEAM CENTERLINE (BLACK) ===}
	mouseover_enabled:=false;
    SP(1); 
    MA(ox, oy);
    DA(round(l/psl*puf)+ox, oy);

	{=== DRAW INITIAL ORDINATE ===}
    SP(1); 
    MA(ox, oy);
    DA(ox, round(r[0]/psm*puf)+oy); 

	{=== DRAW MOMENT/SHEAR DIAGRAM WITH INTERACTIVE POINTS ===}
	mouseover_enabled:=true;
	gWhat := format('story=%d beam=%d',[story, beam]);
	if shear then 
	  appendStr (tabulation_str, '<br>Shear<br>' + gWhat + '<br>') 
	else 
	  appendStr (tabulation_str, '<br>Moment<br>' + gWhat + '<br>');
	  
    if shear then SP(2) else SP(3);  { Red for shear, blue for moment }
    x:=0;
    for i:=0 to k-1 do
      begin
		gX:=x;	gY:=r[i]; { Save for mouseover display }
        DA(round(x/psl*puf)+ox, round(r[i]/psm*puf)+oy);
        x:=x+0.5;
      end;

	{=== FINAL POINT AND CLOSURE ===}
	gX:=l;	gY:=r[k];
    DA( round(l/psl*puf)+ox, round(r[k]/psm*puf)+oy);

	{=== CLOSE DIAGRAM TO BASELINE ===}
	mouseover_enabled:=false;
    SP(1); 
    DA( round(l/psl*puf)+ox, oy);
  end;

{=== BEAM ANALYSIS OUTPUT ===}
procedure output_beam;
{ Calculate and output beam forces, generate beam diagrams }
  var
    i,j,h : integer;
    o,m,m1 : real;
  begin
    {=== TABULAR OUTPUT HEADER ===}
    if not shear then begin
      writeln('Beam':11, 'Moment-L':10, 'Moment-R':10,'Shears-L':10,'Shears-R':10);
      writeln( '(#)':11,'(Kip-ft)':10,'(Kip-ft)':10,'(Kips)':10,'(Kips)':10);
      writeln('----':11,'--------':10,'--------':10,'------':10,'------':10);
    end;
    
    {=== PROCESS EACH STORY FROM TOP TO BOTTOM ===}
    for i:=0 to s-1 do
      begin
        if not shear then writeln('Story',s-i:2);
        
        {=== PROCESS EACH BEAM IN STORY ===}
        for j:=1 to p do
          begin
             H:=I*P+J;  { Beam index in global array }
             O:=B[H,1]/B[H,2];  { Beam stiffness factor }
             
             {=== CALCULATE END MOMENTS FROM JOINT ROTATIONS ===}
             { M = EI/L * (4θᵢ + 2θⱼ) - FEM }
             M:=O*(4*D[S+I+H,Q+1]+2*D[S+I+H+1,Q+1])-B[H,3];   { Left moment }
             M1:=O*(2*D[S+I+H,Q+1]+4*D[S+I+H+1,Q+1])-B[H,4];  { Right moment }
             
             {=== CALCULATE SHEARS FROM EQUILIBRIUM ===}
             { V = (ΣM + applied loads) / L }
             D[H,1]:=(M+M1+B[H,6])/B[H,2];   { Left shear }
             D[H,2]:=(B[H,5]-M-M1)/B[H,2];   { Right shear }
             
             {=== TABULAR OUTPUT ===}
             if not shear then 
               writeln(j:11,m:10:3,m1:10:3,d[h,1]:10:3,d[h,2]:10:3);
               
             {=== GENERATE DIAGRAM ===}
             plot_beam_mom(s-i,j,m,d[h,1]);
          end;
      end;
    if not shear then writeln;
  end;

{=== COLUMN DIAGRAM GENERATION (SIMILAR TO BEAM) ===}
procedure plot_column_mom(story,column:integer; m1,v1:real);
{ Generate SVG for column moment/shear diagram - similar structure to beam plotting }
  var
    w,a,c2,l: real;     { Load parameters }
    sc,ec   : real;     { Start/end column positions }
    bw      : real;     { Beam width offset }
    last_lc : integer;  { Load counter }
    i,k     : integer;  { Loop indices }
    ox,oy   : integer;  { Plot origin }
    x       : real;     { Distance along column }

  {=== FIND LATERAL LOADS AFFECTING THIS COLUMN ===}
  procedure find_load(var w,a,c:real);
    var
      sl,el : real;
    begin
      w:=0;
      while (last_lc<=lc) and (w=0) do
        begin
          { Only process lateral loads (story=0) and only for leftmost column }
          if (ld[lm[last_lc],1]=0) and (column=1) then
            begin
              sl:=ld[lm[last_lc],3];
              el:=sl+ld[lm[last_lc],4];
              if (sl<sc) and (el>sc) then
                begin w:=ld[lm[last_lc],2]; a:=0; c:=el-sc; end
              else if (sl>=sc) and (sl<ec) then
                     begin w:=ld[lm[last_lc],2]; a:=sl-sc; c:=el-sl; end;
              if (w<>0) and (a+c>l) then c:=l-a;
            end;
          last_lc:=last_lc+1;
        end;
    end;

  {=== MOMENT/SHEAR CALCULATION PROCEDURES (SIMILAR TO BEAM) ===}
  procedure tab_moments;
    var
      x,mx : real;
    begin
      x:=0.5; k:=1;
      repeat
        mx:=0;
        if (x>a) and (x<=a+c2) then  mx:=w/2*sqr(x-a)
        else if x>a+c2 then mx:=c2*w*(x-(a+c2/2));
        r[k]:=r[k]-mx;  { Negative for column moment convention }
        x:=x+0.5; k:=k+1;
      until x>=l;
      r[k]:=r[k]-c2*w*(l-(a+c2/2));
    end;

  procedure tab_shears;
    var
      x,vx : real;
    begin
      x:=0.5; k:=1;
      repeat
        vx:=0;
        if (x>a) and (x<=a+c2) then vx:=w*(x-a)
        else if x>a+c2 then vx:=w*c2;
        r[k]:=r[k]+vx;
        x:=x+0.5; k:=k+1;
      until x>=l;
      r[k]:=r[k]+c2*w;
    end;

  {=== MAIN COLUMN PLOTTING ===}
  begin
    {=== CALCULATE COLUMN POSITION ===}
    sc:=0; for i:=1 to story-1 do sc:=sc+c[(p+1)*(s-i)+1,2];
    ec:=sc+c[(p+1)*(s-story)+1,2];
    bw:=0; for i:=1 to column-1 do bw:=bw+b[p*(s-story)+i,2];
    l:=ec-sc;
    
    {=== INITIALIZE AND CALCULATE BASE DIAGRAM ===}
    last_lc:=1;
    for i:=0 to 200 do begin r[i]:=0; end;
    i:=0; x:=0;
    repeat
      if shear then 
        r[i]:=v1 
      else 
        r[i]:=-(m1+v1*x);  { Negative for column moment sign convention }
      i:=i+1; x:=x+0.5;
    until x>=l; 
    
    if shear then r[i]:=v1 else r[i]:=-(m1+v1*l);
    
    {=== ADD DISTRIBUTED LOAD EFFECTS ===}
    repeat
      find_load(w,a,c2);
      if w<>0 then begin 
        if shear then tab_shears else tab_moments; 
      end;
    until last_lc>lc;
    
    k:=i;
    ox:=round( (bw/psl +0.10*(2*column-1)+0.50)*puf );
    oy:=round( (sc/psl +0.10*(2*story-1)      )*puf );

	{=== DRAW COLUMN CENTERLINE ===}
	mouseover_enabled:=false;
    SP(1);
    MA(ox, oy);
    DA(ox, round(l/psl*puf)+oy);

	{=== DRAW INITIAL ORDINATE ===}
    SP(1); 
    MA(ox, oy);
    DA(round(r[0]/psm*puf)+ox, oy); 

	{=== DRAW INTERACTIVE DIAGRAM ===}
	mouseover_enabled:=true;
	gWhat := format('story=%d column=%d',[story, column]);
	if shear then 
	  appendStr(tabulation_str, '<br>Shear<br>' + gWhat + '<br>') 
	else 
	  appendStr(tabulation_str, '<br>Moment<br>' + gWhat + '<br>');
	  
    if shear then SP(2) else SP(3);
    x:=0;
    for i:=0 to k-1 do
      begin
		gX:=x;	gY:=r[i];
        DA(round(r[i]/psm*puf)+ox, round(x/psl*puf)+oy);
        x:=x+0.5;
      end;

	gX:=l;	gY:=r[k];
    DA(round(r[k]/psm*puf)+ox, round(l/psl*puf)+oy);

	mouseover_enabled:=false;
    SP(1); 
    DA(ox, round(l/psl*puf)+oy);

    {=== ADD TITLE AND LEGEND (DONE ONCE AT END) ===}
    if (story=1) and (column=p+1) then
      begin
        MA(100,page_height-30);
        SI(35,35);
        if shear then LA('RIGID FRAME ANALYSIS SHEAR DIAGRAM')
        else		  LA('RIGID FRAME ANALYSIS MOMENT DIAGRAM');
        SI(27, 27);
        MA(100,page_height-60);
        LA('Project Name: '+proj_descrip);
        MA(100,page_height-90);
        LA('Date: '+time);
        MA(100,page_height-120);
        LA('Load Designation: '+load_descrip);
        MA(100,page_height-150);
        if shear then
          	 LA( format( 'Scales: X-%3d feet/inch  Y-%4d Kips/inch ; dots per inch = %3d ', [psl, psm, puf]))
        else LA( format( 'Scales: X-%3d feet/inch  Y-%4d Kip-Feet/inch ; dots per inch = %3d ', [psl, psm, puf]));
        MA(0,0);
      end;
  end;

{=== COLUMN ANALYSIS OUTPUT ===}
procedure output_column;
{ Calculate and output column forces, generate column diagrams }
  var
    i,j,h : integer;
    o,l,m,m1 : real;
  begin
    {=== TABULAR OUTPUT HEADER ===}
    if not shear then begin
      writeln('Column':11,'Moment':10,'Shears':10,'Axial':10);
      writeln( '(#)':11,'(Kip-ft)':10,'(Kips)':10,'(Kips)':10);
      writeln('----':11,'--------':10,'------':10,'------':10);
    end;
    
    {=== PROCESS EACH STORY ===}
    for i:=0 to s-1 do
      begin
        if not shear then writeln('Story',s-i:2);
        
        {=== PROCESS EACH COLUMN IN STORY ===}
        for j:=1 to p+1 do
          begin
            H:=I*(P+1)+J;  { Column index }
            O:=C[H,1]/C[H,2];  { Column stiffness factor }
            L:=6/C[H,2]*D[I+1,Q+1];  { Displacement coupling term }
            
            {=== CALCULATE COLUMN END MOMENTS ===}
            M:=O*(4*D[S+H,Q+1]+2*D[S+H+P+1,Q+1]+L);   { Bottom moment }
            M1:=O*(2*D[S+H,Q+1]+4*D[S+H+P+1,Q+1]+L);  { Top moment }
            
            {=== ADJUST FOR STORY ABOVE ===}
            IF I<>S-1 THEN begin
              L:=O/C[H,2]*6*D[I+2,Q+1];
              M1:=M1-L;
              M:=M-L;
            end;
            
            {=== SUBTRACT WIND FIXED-END MOMENTS ===}
            IF J<=1 THEN begin
              M:=M-E[S-I,2];
              M1:=M1-E[S-I,1];
            end;
            
            {=== CALCULATE AXIAL FORCE (ACCUMULATE FROM ABOVE) ===}
            O:=0;
            IF J<P+1 THEN O:=D[I*P+J,1];     { Beam shear from left }
            IF J>1 THEN O:=O+D[I*P+J-1,2];   { Beam shear from right }
            IF I>0 THEN O:=O+D[J,3];         { Axial from story above }
            D[J,3]:=O;  { Save cumulative axial }
            
            {=== TABULAR OUTPUT WITH SHEAR CALCULATIONS ===}
            L:=0; IF J=1 THEN L:=E[S-I,3];  { Wind shear at bottom }
            if not shear then 
              writeln(j:11,m:10:3,(m+m1-l)/c[h,2]:10:3,o:10:3);
              
            L:=0; IF J=1 THEN L:=E[S-I,4];  { Wind shear at top }
            if not shear then 
              writeln(m1:21:3,(m+m1+l)/-c[h,2]:10:3);
              
            {=== GENERATE COLUMN DIAGRAM ===}
            plot_column_mom(s-i,j,m1,((m+m1+l)/-c[h,2]));
          end;
      end;
    if not shear then writeln;
  end;

{=== DISPLACEMENT AND ROTATION OUTPUT ===}
procedure output_rotation;
{ Output joint rotations and story displacements }
  var
    i,j,l : integer;

  begin
    writeln('Joint':11,'Rotation':15,'Displacement':15);
    writeln('(#)':11,'(Radians)':15,'(Feet)':15);
    writeln('----':11,'---------':15,'------':15);
    L:=0;
    
    {=== PROCESS EACH JOINT ===}
    for i:=s+1 to q do
      begin
        J:=I-S;  { Joint number within story }
        
        {=== CHECK IF AT STORY LEVEL (DISPLACEMENT OUTPUT) ===}
        IF (J-1)/(P+1)=int((J-1)/(P+1)) THEN
          begin
            writeln('Story',trunc(S-(J-1)/(P+1)):2);
            L:=L+1;
            IF L<=S THEN
              { Output both rotation and displacement }
              writeln(j:11,d[i,q+1]*0.0048:15:9,d[l,q+1]*0.0048:15:8)
            else 
              { Output only rotation }
              writeln(j:11,d[i,q+1]*0.0048:15:9);
          end
        else 
          { Interior joint - rotation only }
          writeln(j:11,d[i,q+1]*0.0048:15:9);
      end;
    writeln;
  end;

{=== UTILITY PROCEDURES ===}

procedure reset_matrix;
{ Clear matrices for next load case }
  var
    i,j : integer;
  begin
    for i:=1 to q+1 do for j:=1 to q+1 do d[i,j]:=0;
    for i:=1 to 40  do
      for j:=3 to 6 do
        begin
          b[i,j]:=0;
          if i<11 then e[i,j-2]:=0;
        end;
  end;

procedure user_option;
{ Display menu and get user choice }
  begin
    writeln('============================================');
    writeln('1. Define More Macros.':30);
    writeln('2. Reload the Frame.  ':30);
    writeln('3. Exit Program.      ':30);
    writeln;
    repeat
      option:=0;
      write('Enter Option ? '); readi(option); writeln(option:3);
    until option in [1,2,3];
    writeln;
  end;

{=== MAIN PROGRAM ===}
begin 
	{=== GENERATE JAVASCRIPT FOR INTERACTIVE FEATURES ===}
	writeln('<script>');
	writeln('var last_o=null;');
	writeln('function showTab(id_no){ let id=`tab${id_no}`; let o=document.getElementById(id); o.scrollIntoView({behavior: "auto", block: "nearest", inline: "nearest"}); o.style.backgroundColor="lightblue"; if (last_o != null) last_o.style.backgroundColor="white"; last_o=o;}');
	writeln('</script>');

	{=== PROGRAM HEADER ===}
  writeln('RIGID FRAME ANALYSIS  By Gary Argraves - Ver. 8809.08');
  writeln('=====================================================');
  time:=when;
  signon;

  {=== MAIN ANALYSIS LOOP ===}
  initial_var;
  get_size;
  get_geometry;
  get_member_sizes;
  get_base_joint_type;
  option:=1;
  
  repeat
    if option=1 then get_macro_loads;
    apply_macro_loads;
    setup_matrix;
    solve_matrix;

	{=== GENERATE MOMENT DIAGRAMS ===}
	writeln;
	tabulation_str:='<b>'+load_descrip+'</b><br>';
	plot_str:= format( '<svg width="%d" height="%d">', [page_width, page_height]);
	{ Draw page border }
	SP(1); MA(0,0); DA(0,page_height); DA(page_width, page_height); DA(page_width,0);  DA(0,0);

	shear:=false; output_beam; output_column;
	appendStr( plot_str , '</svg>');

	{=== OUTPUT MOMENT DIAGRAM WITH INTERACTIVE TABULATION ===}
	writeln('<table><tr>');
	writeln('<td>');
		writeln('<div style="height:700px; width:180px; border:1px solid black; overflow-y:scroll; margin:10 10 10 10;padding:10 10 10 10;">');
		writeln(tabulation_str);
		writeln('</div>');
	writeln('<td>');
		writeln(plot_str);
	writeln('</tr></table>');
  	
	{=== GENERATE SHEAR DIAGRAMS ===}
	writeln;
	tabulation_str:='<b>'+load_descrip+'</b><br>';
	plot_str:=  format( '<svg width="%d" height="%d">', [page_width, page_height]);
	{ Draw page border }
	SP(1); MA(0,0); DA(0,page_height); DA(page_width, page_height); DA(page_width,0);  DA(0,0);

	shear:=true;  output_beam; output_column;
	appendStr( plot_str , '</svg>');

	{=== OUTPUT SHEAR DIAGRAM WITH INTERACTIVE TABULATION ===}
	writeln('<table><tr>');
	writeln('<td>');
		writeln('<div style="height:700px; width:180px; border:1px solid black; overflow-y:scroll; margin:10 10 10 10;padding:10 10 10 10;">');
		writeln(tabulation_str);
		writeln('</div>');




	writeln('<td>');
		writeln(plot_str);
	writeln('</tr></table>');

    {=== OUTPUT NUMERICAL RESULTS ===}
    output_rotation;
    reset_matrix;
    user_option;
  until option=3;
  
  {=== PROGRAM COMPLETION ===}
  writeln('*****  E N D   O F   R U N  *****');
end.

{
================================================================================
# END FILE: framer_plot_commented.pas
================================================================================
}
