-- File: SVTransformsImpl.mesa
-- Last edited by Bier on December 18, 1982 1:34 am
-- Author: Eric Bier before July 3, 1983 1:30 pm
-- Contents: Procedures for Scaling, Translating, and Rotating assemblies with respect to an arbitrary coordinate system

DIRECTORY
 CoordSys,
 CSGGraphics,
 DisplayList3d,
 MessageWindow,
 Matrix3d,
 SVTransforms,
 SVVector3d;

SVTransformsImpl: PROGRAM
IMPORTS CoordSys, DisplayList3d, Matrix3d, MessageWindow, SVVector3d
EXPORTS SVTransforms =

BEGIN

Assembly: TYPE = DisplayList3d.Assembly;
AssemblyList: TYPE = DisplayList3d.AssemblyList;
Camera: TYPE = CSGGraphics.Camera;
CoordSystem: TYPE = CoordSys.CoordSystem;
MasterObject: TYPE = DisplayList3d.MasterObject;
Matrix4by4: TYPE = Matrix3d.Matrix4by4;
Scene: TYPE = DisplayList3d.Scene;
Vector: TYPE = SVVector3d.Vector;


TellAboutCameraAndWorld1: PUBLIC PROC [a: Assembly, camera: Camera, scene: Scene] = {
-- tree walk through the tree to the leaves then concatenate all matrices (will later concatenate matrices as it goes).
camera.coordSys.wrtWorld ← CoordSys.FindInTermsOfWorld[camera.coordSys];
FOR list: LIST OF Assembly ← DisplayList3d.ListOfPrimAssemblies[a, scene], list.rest
UNTIL list = NIL DO
 list.first.coordSys.wrtCamera ←
  Matrix3d.LocalScale[CoordSys.FindInTermsOfCamera[list.first.coordSys, camera.coordSys],
  list.first.scalars[1], list.first.scalars[2], list.first.scalars[3]];
 list.first.coordSys.wrtWorld ←
  Matrix3d.LocalScale[CoordSys.FindInTermsOfWorld[list.first.coordSys],
        list.first.scalars[1], list.first.scalars[2], list.first.scalars[3]];
 list.first.coordSys.cameraWRTlocal ← Matrix3d.Inverse[list.first.coordSys.wrtCamera];
 list.first.coordSys.worldWRTlocal ← Matrix3d.Inverse[list.first.coordSys.wrtWorld];
ENDLOOP;
}; -- end of TellAboutCameraAndWorld

TellAboutCameraAndWorld: PUBLIC PROC [a: Assembly, camera: Camera, scene: Scene] = {
-- tree walk through the tree and concatenate all matrices (will later concatenate matrices as it goes).
 camera.coordSys.wrtWorld ← CoordSys.FindInTermsOfWorld[camera.coordSys];
WITH a.object SELECT FROM
 mo: MasterObject => {
  a.coordSys.wrtCamera ←
   Matrix3d.LocalScale[CoordSys.FindInTermsOfCamera[a.coordSys, camera.coordSys],
   a.scalars[1], a.scalars[2], a.scalars[3]];
  a.coordSys.wrtWorld ←
   Matrix3d.LocalScale[CoordSys.FindInTermsOfWorld[a.coordSys],
        a.scalars[1], a.scalars[2], a.scalars[3]];
  a.coordSys.cameraWRTlocal ← Matrix3d.Inverse[a.coordSys.wrtCamera];
  a.coordSys.worldWRTlocal ← Matrix3d.Inverse[a.coordSys.wrtWorld];
  };
 alist: AssemblyList => {
  a.coordSys.wrtCamera ←
   CoordSys.FindInTermsOfCamera[a.coordSys, camera.coordSys];
  a.coordSys.wrtWorld ←
   CoordSys.FindInTermsOfWorld[a.coordSys];
  a.coordSys.cameraWRTlocal ← Matrix3d.Inverse[a.coordSys.wrtCamera];
  a.coordSys.worldWRTlocal ← Matrix3d.Inverse[a.coordSys.wrtWorld];
  TellAboutCameraAndWorldInList[alist, camera, scene];
  };
ENDCASE => ERROR;
 }; -- end of TellAboutCameraAndWorld

TellAboutCameraAndWorldInList: PRIVATE PROC [alist: AssemblyList, camera: Camera, scene: Scene] = {
FOR list: LIST OF Assembly ← alist.list, list.rest UNTIL list = NIL DO
  TellAboutCameraAndWorld[list.first, camera, scene];
ENDLOOP;
 };


Translate: PUBLIC PROC [a: Assembly, cs: CoordSystem, tx, ty, tz: REAL] = {
-- simple translate a's coordsys wrt cs by [tx, ty, tz];
 CoordSys.TTranslateAwrtB[a.coordSys, cs, tx, ty, tz];
 };

XRotate: PUBLIC PROC [a: Assembly, cs: CoordSystem, degrees: REAL] = {
-- simple rotate a's coordsys wrt cs by degrees about the x axis
 CoordSys.TXRotateAwrtB[a.coordSys, cs, degrees];
 };

YRotate: PUBLIC PROC [a: Assembly, cs: CoordSystem, degrees: REAL] = {
-- simple rotate a's coordsys wrt cs by degrees about the y axis
 CoordSys.TYRotateAwrtB[a.coordSys, cs, degrees];
 };

ZRotate: PUBLIC PROC [a: Assembly, cs: CoordSystem, degrees: REAL] = {
-- simple rotate a's coordsys wrt cs by degrees about the z axis
 CoordSys.TZRotateAwrtB[a.coordSys, cs, degrees];
 };

-- There are four ways to scale a cluster assembly
-- 1) (ScalePrimitives) Scale each primitive in place, evenly or differentially.
-- 2) (ScaleEven) If sx = sy = sz. Scale primitives in place and then translate all intermediate assemblies so the cluster assembly scales evenly as a unit
-- 3) (ScaleAndShear) IF sx # sy or sy # sz then scale and shear the primitives and translate them differentially so the cluster assembly scales and shears as a unit.
-- 4) (ScaleNoShear) IF sx # sy or sy # sz. Scale the primitives evenly so that their dimension in the direction of scaling increases or decreases to the same quantity it would have reached by method 3. This preserves shape of primitives. Global shape is not preserved (but almost).

ScalePrimitive: PUBLIC PROC [a: Assembly, sx, sy, sz: REAL] = {
IF NOT ISTYPE[a.object, MasterObject] THEN ERROR;
 a.scalars[1] ← a.scalars[1]*sx;
 a.scalars[2] ← a.scalars[2]*sy;
 a.scalars[3] ← a.scalars[3]*sz;
 };

ScalePrimitives: PUBLIC PROC [a: Assembly, sx, sy, sz: REAL] = {
WITH a.object SELECT FROM
 mo: MasterObject => ScalePrimitive[a, sx, sy, sz];
 alist: AssemblyList => {
  FOR list: LIST OF Assembly ← alist.list, list.rest UNTIL list = NIL DO
   ScalePrimitives[list.first, sx, sy, sz];
  ENDLOOP;
  };
ENDCASE; -- IF a.object = NIL or something else, do nothing.
 };

ScaleEven: PUBLIC PROC [a: Assembly, cs: CoordSystem, scalar: REAL] = {
 displacements: Vector;
WITH a.object SELECT FROM
 mo: MasterObject => {
   ScalePrimitive[a, scalar, scalar, scalar];
  -- now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar
  displacements ← CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, cs];
  displacements ← SVVector3d.Scale[displacements, scalar];
  CoordSys.TPlaceTranslationAwrtB[a.coordSys, cs, displacements];
  };
 alist: AssemblyList => {
  FOR list: LIST OF Assembly ← alist.list, list.rest UNTIL list = NIL DO
   ScaleEven[list.first, a.coordSys, scalar]; -- recursively scale subassemblies with respect to their own coordinate systems
  
ENDLOOP;
  -- now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar
  displacements ← CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, cs];
  displacements ← SVVector3d.Scale[displacements, scalar];
  CoordSys.TPlaceTranslationAwrtB[a.coordSys, cs, displacements];
  };
ENDCASE; -- IF a.object = NIL or something else, do nothing.
 };

Scale: PUBLIC PROC [a: Assembly, cs: CoordSystem, sx, sy, sz: REAL] = {
IF ISTYPE[a.object, MasterObject] AND cs = a.coordSys THEN ScalePrimitive[a, sx, sy, sz]
ELSE ScaleEven[a, cs, sx];
 };

Align: PUBLIC PROC [a: Assembly, cs: CoordSystem] = {
-- aligning is a pure rotation so simply align a's coordsys wrt cs.
 CoordSys.TAlignAwrtB[a.coordSys, cs];
 };

Abut: PUBLIC PROC [a: Assembly, cs: CoordSystem] = {
-- abutting is a pure translation so simply abut a's coordsys wrt cs.
 CoordSys.TAbutAwrtB[a.coordSys, cs];
 };

AbutX: PUBLIC PROC [a: Assembly, cs: CoordSystem] = {
-- abutting is a pure translation so simply abut a's coordsys wrt cs.
 CoordSys.TAbutXAwrtB[a.coordSys, cs];
 };

AbutY: PUBLIC PROC [a: Assembly, cs: CoordSystem] = {
-- abutting is a pure translation so simply abut a's coordsys wrt cs.
 CoordSys.TAbutYAwrtB[a.coordSys, cs];
 };

AbutZ: PUBLIC PROC [a: Assembly, cs: CoordSystem] = {
-- abutting is a pure translation so simply abut a's coordsys wrt cs.
 CoordSys.TAbutZAwrtB[a.coordSys, cs];
 };

Place: PUBLIC PROC [a: Assembly, cs: CoordSystem, mat: Matrix4by4] = {
-- mat may have only translational and rotational components. Check to make sure this is so and then call the CoordSys PlaceAwrtB.
IF MatrixHasScaling[mat]
  THEN {MessageWindow.Append["SVTransformImpl.Place got illegal matrix. Ignored."];
     RETURN};
 CoordSys.TPlaceAwrtB[a.coordSys, cs, mat];
 };

MatrixHasScaling: PRIVATE PROC [mat: Matrix4by4] RETURNS [BOOL] = {
 sx, sy, sz: REAL;
 almostZero: REAL ← 1.0e-4;
 [sx, sy, sz] ← Matrix3d.ScaleFromMatrix[mat];
IF ABS[sx - 1.0] > almostZero OR ABS[sy-1.0] > almostZero OR ABS[sz - 1.0] > almostZero
  THEN RETURN[TRUE]
  ELSE RETURN [FALSE];
 };

AlignIndirect: PUBLIC PROC [a: Assembly, tugBoat: CoordSystem, dock: CoordSystem] = {
-- One way to proceed is this:
-- 1) Find the a WRT tugBoat.
-- 2) Align tugboat with dock.
-- 3) Place a WRT tugBoat using (1)
 aWRTtugBoat: Matrix4by4 ← CoordSys.FindAinTermsOfB[a.coordSys, tugBoat];
 CoordSys.TAlignAwrtB[tugBoat, dock];
 Place[a, tugBoat, aWRTtugBoat];
 };

AbutIndirect: PUBLIC PROC [a: Assembly, tugBoat: CoordSystem, dock: CoordSystem] = {
 aWRTtugBoat: Matrix4by4 ← CoordSys.FindAinTermsOfB[a.coordSys, tugBoat];
 CoordSys.TAbutAwrtB[tugBoat, dock];
 Place[a, tugBoat, aWRTtugBoat];
 };

PlaceIndirect: PUBLIC PROC [a: Assembly, tugBoat: CoordSystem, dock: CoordSystem, mat: Matrix4by4] = {
 aWRTtugBoat: Matrix4by4;
IF MatrixHasScaling[mat]
THEN {MessageWindow.Append["SVTransformImpl.PlaceIndirect got illegal matrix. Ignored."];
    RETURN};
 aWRTtugBoat ← CoordSys.FindAinTermsOfB[a.coordSys, tugBoat];
 CoordSys.TPlaceAwrtB[tugBoat, dock, mat];
 Place[a, tugBoat, aWRTtugBoat];
 };

END.