<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D profile='Immersive' version='3.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.0.xsd'>
  <head>
    <meta content='SimpleBuildingConstructionPrototypes.x3d' name='title'/>
    <meta content='Don Brutzman' name='creator'/>
    <meta content='17 October 2001' name='created'/>
    <meta content='20 October 2019' name='modified'/>
    <meta content='Prototypes for simple building construction: Floor, Wall, Level and Building.' name='description'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Savage/Buildings/UHRB/SimpleBuildingConstructionPrototypes.x3d' name='identifier'/>
    <meta content='X3D-Edit 3.2, https://www.web3d.org/x3d/tools/X3D-Edit' name='generator'/>
    <meta content='../../license.html' name='license'/>
  </head>
  <Scene>
    <WorldInfo title='SimpleBuildingConstructionPrototypes.x3d'/>
    <ProtoDeclare appinfo='Each Floor cantains the current floor surface plus a ceiling surface for the floor immediately underneath. A Floor does not include exterior or interior wall polygons.' name='Floor'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Identifying name for this Floor.' name='name' type='SFString'/>
        <field accessType='initializeOnly' appinfo='Description info for this construction.' name='description' type='MFString'/>
        <field accessType='initializeOnly' appinfo='single-value Coordinate node with dimension x=width y=height z=depth in meters' name='size' type='SFNode'>
          <Coordinate/>
        </field>
        <field accessType='initializeOnly' appinfo='Appearance node with Material colors ImageTexture etc. for this construction.' name='floorAppearance' type='SFNode'>
          <Appearance DEF='DefaultFloorAppearance'>
            <Material diffuseColor='0.2 0.2 0.2'/>
          </Appearance>
        </field>
        <field accessType='initializeOnly' appinfo='Appearance node with Material colors ImageTexture etc. for this construction.' name='ceilingAppearance' type='SFNode'>
          <Appearance DEF='DefaultCeilingAppearance'>
            <Material/>
          </Appearance>
        </field>
        <field accessType='initializeOnly' appinfo='Whether sides are visible.' name='showSides' type='SFBool' value='false'/>
        <field accessType='outputOnly' appinfo='width of front side of floor aligned with local X axis.' name='width' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='height of vertical distance between floor and ceiling directly underneath aligned with local Y axis.' name='height' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='depth of horizontal side of floor aligned with local -Z axis.' name='depth' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Indicate whether initialization complete.' name='built' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Group DEF='FloorRoot'>
          <Transform DEF='LowerLeftOutsideCornerLocation'>
            <Shape DEF='Floor'>
              <IS>
                <connect nodeField='appearance' protoField='floorAppearance'/>
              </IS>
              <IndexedFaceSet coordIndex='0 1 2 3 0 -1'>
                <Coordinate DEF='FloorCoordinate'/>
              </IndexedFaceSet>
            </Shape>
            <Shape DEF='Ceiling'>
              <IS>
                <connect nodeField='appearance' protoField='ceilingAppearance'/>
              </IS>
              <IndexedFaceSet coordIndex='4 7 6 5 4 -1'>
                <Coordinate USE='FloorCoordinate'/>
              </IndexedFaceSet>
            </Shape>
            <Switch DEF='FloorSidesSwitch' whichChoice='-1'>
              <Shape DEF='FloorSides'>
                <IS>
                  <connect nodeField='appearance' protoField='floorAppearance'/>
                </IS>
                <IndexedFaceSet coordIndex='0 3 7 4 -1 3 2 6 7 -1 1 5 6 2 -1 0 4 5 1 -1' solid='false'>
                  <Coordinate USE='FloorCoordinate'/>
                </IndexedFaceSet>
              </Shape>
            </Switch>
          </Transform>
          <Script DEF='FloorConstructionScript' directOutput='true'>
            <field accessType='initializeOnly' name='name' type='SFString'/>
            <field accessType='initializeOnly' name='description' type='MFString'/>
            <field accessType='initializeOnly' name='size' type='SFNode'/>
            <field accessType='initializeOnly' name='showSides' type='SFBool'/>
            <field accessType='outputOnly' name='wallsVisible' type='SFInt32'/>
            <field accessType='outputOnly' name='floorPoints' type='MFVec3f'/>
            <field accessType='outputOnly' name='width' type='SFFloat'/>
            <field accessType='outputOnly' name='height' type='SFFloat'/>
            <field accessType='outputOnly' name='depth' type='SFFloat'/>
            <field accessType='initializeOnly' name='traceEnabled' type='SFBool' value='true'/>
            <field accessType='outputOnly' name='built' type='SFBool'/>
            <IS>
              <connect nodeField='name' protoField='name'/>
              <connect nodeField='description' protoField='description'/>
              <connect nodeField='size' protoField='size'/>
              <connect nodeField='showSides' protoField='showSides'/>
              <connect nodeField='width' protoField='width'/>
              <connect nodeField='height' protoField='height'/>
              <connect nodeField='depth' protoField='depth'/>
              <connect nodeField='built' protoField='built'/>
            </IS>
            <![CDATA[
ecmascript:

function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[Floor' + name + ']' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[Floor' + name + ']' + outputString);
}
function initialize ()
{
	built = false;
	tracePrint ('description=' + description);
	tracePrint ('showSides=' + showSides);
	if (showSides == true) wallsVisible = 0; // goes to Switch whichChoice
	tracePrint ('wallsVisible=' + wallsVisible);
	if ((size.point.length == 0) || (size.point.length > 1))
		alwaysPrint ('** warning, size.point.length =' + size.point.length + ' rather than 1');
	width  = size.point[0].x;
	height = size.point[0].y;
	depth  = size.point[0].z;
	tracePrint ('(width, height, depth)=(' + width + ',' + height + ',' +  depth + ')');
	// floor is immediately above ceiling
	floorPoints = new MFVec3f (
		new SFVec3f (0, 0, 0),
		new SFVec3f (width, 0, 0),
		new SFVec3f (width, 0, -depth),
		new SFVec3f (0, 0, -depth),
		new SFVec3f (0, -height, 0),
		new SFVec3f (width, -height, 0),
		new SFVec3f (width, -height, -depth),
		new SFVec3f (0, -height, -depth));
	tracePrint ('floorPoints=' + floorPoints);
	built = true;
	tracePrint ('built=' + built);
}
]]>
          </Script>
          <ROUTE fromField='floorPoints' fromNode='FloorConstructionScript' toField='point' toNode='FloorCoordinate'/>
          <ROUTE fromField='wallsVisible' fromNode='FloorConstructionScript' toField='whichChoice' toNode='FloorSidesSwitch'/>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <ProtoDeclare appinfo='Each Wall contains exterior and interior walls.' name='Wall'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Identifying name for this Wall.' name='name' type='SFString'/>
        <field accessType='initializeOnly' appinfo='Description info for this construction.' name='description' type='MFString'/>
        <field accessType='initializeOnly' appinfo='single-value Coordinate node with dimension x=width y=height z=depth in meters' name='size' type='SFNode'>
          <Coordinate point='0 0 0'/>
        </field>
        <field accessType='initializeOnly' appinfo='Appearance node with Material colors ImageTexture etc. for this construction.' name='interiorAppearance' type='SFNode'>
          <Appearance DEF='DefaultInteriorAppearance'>
            <Material diffuseColor='0.4 0.4 0.4'/>
          </Appearance>
        </field>
        <field accessType='initializeOnly' appinfo='Appearance node with Material colors ImageTexture etc. for this construction.' name='exteriorAppearance' type='SFNode'>
          <Appearance DEF='DefaultExteriorAppearance'>
            <Material diffuseColor='0.6 0.6 0.6'/>
          </Appearance>
        </field>
        <field accessType='initializeOnly' appinfo='Whether sides are visible.' name='showSides' type='SFBool' value='false'/>
        <field accessType='outputOnly' appinfo='width of horizontal side of wall aligned with local X axis.' name='width' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='height of Wall aligned with local Y axis.' name='height' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='depth of horizontal thickness of Wall aligned with local -Z axis.' name='depth' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Indicate whether initialization complete.' name='built' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Group DEF='WallRoot'>
          <Transform DEF='LowerLeftOutsideCornerLocationWall'>
            <Shape DEF='InteriorWall'>
              <IS>
                <connect nodeField='appearance' protoField='interiorAppearance'/>
              </IS>
              <!-- only draw interior-facing side for efficiency, and also so that view piercing external wall immediately sees interior -->
              <IndexedFaceSet coordIndex='4 7 6 5 4 -1'>
                <Coordinate DEF='WallCoordinate'/>
              </IndexedFaceSet>
            </Shape>
            <Shape DEF='ExteriorWall'>
              <IS>
                <connect nodeField='appearance' protoField='exteriorAppearance'/>
              </IS>
              <IndexedFaceSet coordIndex='0 1 2 3 0 -1'>
                <Coordinate USE='WallCoordinate'/>
              </IndexedFaceSet>
            </Shape>
            <Switch DEF='WallSidesSwitch' whichChoice='-1'>
              <Shape DEF='WallSides'>
                <IS>
                  <connect nodeField='appearance' protoField='exteriorAppearance'/>
                </IS>
                <IndexedFaceSet coordIndex='0 3 7 4 -1 3 2 6 7 -1 1 5 6 2 -1 0 4 5 1 -1'>
                  <Coordinate USE='WallCoordinate'/>
                </IndexedFaceSet>
              </Shape>
            </Switch>
          </Transform>
          <Script DEF='WallConstructionScript' directOutput='true'>
            <field accessType='initializeOnly' name='name' type='SFString'/>
            <field accessType='initializeOnly' name='description' type='MFString'/>
            <field accessType='initializeOnly' name='size' type='SFNode'/>
            <field accessType='initializeOnly' name='showSides' type='SFBool'/>
            <field accessType='outputOnly' name='wallsVisible' type='SFInt32'/>
            <field accessType='outputOnly' name='coordinatePoints' type='MFVec3f'/>
            <field accessType='outputOnly' name='width' type='SFFloat'/>
            <field accessType='outputOnly' name='height' type='SFFloat'/>
            <field accessType='outputOnly' name='depth' type='SFFloat'/>
            <field accessType='initializeOnly' name='traceEnabled' type='SFBool' value='true'/>
            <field accessType='outputOnly' name='built' type='SFBool'/>
            <IS>
              <connect nodeField='name' protoField='name'/>
              <connect nodeField='description' protoField='description'/>
              <connect nodeField='size' protoField='size'/>
              <connect nodeField='showSides' protoField='showSides'/>
              <connect nodeField='width' protoField='width'/>
              <connect nodeField='height' protoField='height'/>
              <connect nodeField='depth' protoField='depth'/>
              <connect nodeField='built' protoField='built'/>
            </IS>
            <![CDATA[
ecmascript:

function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[Wall' + name + ']' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[Wall' + name + ']' + outputString);
}
function initialize ()
{
	built = false;
	tracePrint ('description=' + description);
	tracePrint ('showSides=' + showSides);
	if (showSides == true) wallsVisible = 0; // goes to Switch whichChoice
	tracePrint ('wallsVisible=' + wallsVisible);
	if ((size.point.length == 0) || (size.point.length > 1))
		alwaysPrint ('** warning, size.point.length =' + size.point.length + ' rather than 1');
	width  = size.point[0].x;
	height = size.point[0].y;
	depth  = size.point[0].z;
	tracePrint ('(width, height, depth)=(' + width + ',' + height + ',' +  depth + ')');
	coordinatePoints = new MFVec3f (
		new SFVec3f (0, 0, 0),
		new SFVec3f (width, 0, 0),
		new SFVec3f (width, height, 0),
		new SFVec3f (0, height, 0),
		new SFVec3f (0, 0, -depth),
		new SFVec3f (width, 0, -depth),
		new SFVec3f (width, height, -depth),
		new SFVec3f (0, height, -depth));
	tracePrint ('coordinatePoints=' + coordinatePoints);
	built = true;
	tracePrint ('built=' + built);
}
]]>
          </Script>
          <ROUTE fromField='coordinatePoints' fromNode='WallConstructionScript' toField='point' toNode='WallCoordinate'/>
          <ROUTE fromField='wallsVisible' fromNode='WallConstructionScript' toField='whichChoice' toNode='WallSidesSwitch'/>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <ProtoDeclare appinfo='collection of a Floor and four Walls working in order up from lowest level (i.e. story) of the Building' name='Level'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Identifying name for this Level.' name='name' type='SFString'/>
        <field accessType='initializeOnly' appinfo='Description info for this construction.' name='description' type='MFString'/>
        <field accessType='initializeOnly' appinfo='contains single Floor node' name='floor' type='MFNode'>
          <Group/>
        </field>
        <field accessType='initializeOnly' appinfo='contains single front Wall node' name='frontWall' type='MFNode'>
          <ProtoInstance name='Wall'/>
        </field>
        <field accessType='initializeOnly' appinfo='contains single right-side Wall node' name='rightWall' type='MFNode'>
          <ProtoInstance name='Wall'/>
        </field>
        <field accessType='initializeOnly' appinfo='contains single rear Wall node' name='rearWall' type='MFNode'>
          <ProtoInstance name='Wall'/>
        </field>
        <field accessType='initializeOnly' appinfo='contains single left-side Wall node' name='leftWall' type='MFNode'>
          <ProtoInstance name='Wall'/>
        </field>
        <field accessType='outputOnly' appinfo='Calculated width of horizontal side of Level aligned with local X axis.' name='width' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Calculated height of Level aligned with local Y axis.' name='height' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Calculated depth of horizontal thickness of Level aligned with local -Z axis.' name='depth' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Indicate whether initialization complete.' name='built' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Group DEF='LevelRoot'>
          <Transform DEF='FloorTransform'>
            <IS>
              <connect nodeField='children' protoField='floor'/>
            </IS>
          </Transform>
          <Transform DEF='FrontWallTransform'>
            <IS>
              <connect nodeField='children' protoField='frontWall'/>
            </IS>
          </Transform>
          <Transform DEF='RightWallTransform' rotation='0 1 0 1.57079'>
            <IS>
              <connect nodeField='children' protoField='rightWall'/>
            </IS>
          </Transform>
          <Transform DEF='RearWallTransform' rotation='0 1 0 3.1416'>
            <IS>
              <connect nodeField='children' protoField='rearWall'/>
            </IS>
          </Transform>
          <Transform DEF='LeftWallTransform' rotation='0 1 0 4.7124'>
            <IS>
              <connect nodeField='children' protoField='leftWall'/>
            </IS>
          </Transform>
          <Script DEF='LevelConstructionScript' directOutput='true'>
            <field accessType='initializeOnly' name='name' type='SFString'/>
            <field accessType='initializeOnly' name='description' type='MFString'/>
            <field accessType='initializeOnly' name='floor' type='MFNode'/>
            <field accessType='initializeOnly' name='frontWall' type='MFNode'/>
            <field accessType='initializeOnly' name='rightWall' type='MFNode'/>
            <field accessType='initializeOnly' name='rearWall' type='MFNode'/>
            <field accessType='initializeOnly' name='leftWall' type='MFNode'/>
            <field accessType='outputOnly' name='width' type='SFFloat'/>
            <field accessType='outputOnly' name='height' type='SFFloat'/>
            <field accessType='outputOnly' name='depth' type='SFFloat'/>
            <field accessType='outputOnly' name='rightTranslation' type='SFVec3f'/>
            <field accessType='outputOnly' name='rearTranslation' type='SFVec3f'/>
            <field accessType='outputOnly' name='leftTranslation' type='SFVec3f'/>
            <field accessType='initializeOnly' name='traceEnabled' type='SFBool' value='true'/>
            <field accessType='inputOnly' appinfo='New floor or wall initialization may be complete check and recalculate until built' name='recheckUntilBuilt' type='SFTime'/>
            <field accessType='outputOnly' name='built' type='SFBool'/>
            <IS>
              <connect nodeField='name' protoField='name'/>
              <connect nodeField='description' protoField='description'/>
              <connect nodeField='floor' protoField='floor'/>
              <connect nodeField='frontWall' protoField='frontWall'/>
              <connect nodeField='rightWall' protoField='rightWall'/>
              <connect nodeField='rearWall' protoField='rearWall'/>
              <connect nodeField='leftWall' protoField='leftWall'/>
              <connect nodeField='width' protoField='width'/>
              <connect nodeField='height' protoField='height'/>
              <connect nodeField='depth' protoField='depth'/>
              <connect nodeField='built' protoField='built'/>
            </IS>
            <![CDATA[
ecmascript:

var firstLoopWhileTrueCompleted;

function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[Level' + name + ']' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[Level' + name + ']' + outputString);
}
function initialize ()
{
	built = false;
	firstLoopWhileTrueCompleted = false;
	alwaysPrint ('initialize, built=' + built);
}
function recheckUntilBuilt (value)
{
	if      (built == true)
	{
		built = true; // resend to trigger cancellation event for TimeSensor
		if (firstLoopWhileTrueCompleted)
			alwaysPrint ('recheckUntilBuilt() continuous built=true indicates internal error');
		else firstLoopWhileTrueCompleted = true;
		return;  // done
	}
	alwaysPrint ('recheckUntilBuilt testing...');
	// following are single nodes, cast as MFNode type for IS/connect matchups
	if      (floor[0].built == false)	return;  // not yet ready
	else if (frontWall[0].built == false)	return;
	else if (rightWall[0].built == false)	return;
	else if (rearWall[0].built == false)	return;
	else if (leftWall[0].built == false)	return;
	alwaysPrint ('recheckUntilBuilt ready, initializing Level...');

	alwaysPrint ('description=' + description);
	if (floor)
	{
	//	tracePrint ('floor found');
		if (floor.length > 1)
			alwaysPrint ('** warning, more than one floor found (' +
				floor.length + ' nodes total)');
		tracePrint ('floor (width, height, depth)=(' +
			floor[0].width + ',' + floor[0].height + ',' +  floor[0].depth + ')');
	}
	else	tracePrint ('floor not found');

	if (frontWall)
	{
	//	tracePrint ('frontWall found');
		if (frontWall > 1)
			alwaysPrint ('** warning, more than one frontWall found (' +
				frontWall.length + ' nodes total)');
		tracePrint ('frontWall (width, height, depth)=(' +
			frontWall[0].width + ',' + frontWall[0].height + ',' +  frontWall[0].depth + ')');
	}
	else	tracePrint ('frontWall not found');

	if (rightWall)
	{
	//	tracePrint ('rightWall found');
		if (rightWall > 1)
			alwaysPrint ('** warning, more than one rightWall found (' +
				frontWall.length + ' nodes total)');
		tracePrint ('rightWall (width, height, depth)=(' +
			rightWall[0].width + ',' + rightWall[0].height + ',' +  rightWall[0].depth + ')');
	}

	else	tracePrint ('rightWall not found');

	if (rearWall)
	{
	//	tracePrint ('rearWall found');
		if (frontWall > 1)
			alwaysPrint ('** warning, more than one rearWall found (' +
				rearWall + ' nodes total)');
		tracePrint ('rearWall (width, height, depth)=(' +
			rearWall[0].width + ',' + rearWall[0].height + ',' +  rearWall[0].depth + ')');
	}
	else	tracePrint ('rearWall not found');

	if (leftWall)
	{
	//	tracePrint ('leftWall found');
		if (frontWall > 1)
			alwaysPrint ('** warning, more than one leftWall found (' +
				leftWall.length + ' nodes total)');
		tracePrint ('leftWall (width, height, depth)=(' +
			leftWall[0].width + ',' + leftWall[0].height + ',' +  leftWall[0].depth + ')');
	}
	else	tracePrint ('leftWall not found');

	if (floor && frontWall)
	{
		if ((floor[0].width != frontWall[0].width) && (floor[0].width != 0) && (frontWall[0].width != 0))
			alwaysPrint ('** warning, floor/frontWall width mismatch');
	}
	if (floor && rearWall)
	{
		if ((floor[0].width != rearWall[0].width) && (floor[0].width != 0) && (rearWall[0].width != 0))
			alwaysPrint ('** warning, floor/rearWall width mismatch');
	}
	if (floor && rightWall)
	{
		if ((floor[0].depth != rightWall[0].width) && (floor[0].depth != 0) && (rightWall[0].width != 0))
			alwaysPrint ('** warning, floor.depth/rightWall.width mismatch');
	}
	if (floor && leftWall)
	{
		if ((floor[0].depth != leftWall[0].width) && (floor[0].depth != 0) && (leftWall[0].width != 0))
			alwaysPrint ('** warning, floor.depth/leftWall.width mismatch');
	}
	if (frontWall && rearWall)
	{
		if ((frontWall[0].width != rearWall[0].width) && (frontWall[0].width != 0) && (rearWall[0].width != 0))
			alwaysPrint ('** warning, frontWall/rearWall width mismatch');
	}
	if (leftWall && rightWall)
	{
		if ((leftWall[0].width != rightWall[0].width) && (leftWall[0].width != 0) && (rightWall[0].width != 0))
			alwaysPrint ('** warning, leftWall/rightWall width mismatch');
	}

	// find first nonzero values
	width = 0;
	if (floor)                     width = floor[0].width;
	if (frontWall && (width == 0)) width = frontWall[0].width;
	if (rearWall  && (width == 0)) width = rearWall[0].width;

	height = 0;
	if      (frontWall)                  height = frontWall[0].height;
	if      (rightWall && (height == 0)) height = rightWall[0].height;
	else if (rearWall  && (height == 0)) height = rearWall[0].height;
	else if (leftWall  && (height == 0)) height = leftWall[0].height;

	depth = 0;
	if (floor)                     depth = floor[0].depth;
	if (rightWall && (depth == 0)) depth = rightWall[0].depth;
	if (leftWall  && (depth == 0)) depth = leftWall[0].depth;

	tracePrint ('(width, height, depth)=(' + width + ',' + height + ',' +  depth + ')');

	// translate wall centers (not corners)
	rightTranslation = new SFVec3f (width, 0, 0);
	tracePrint ('rightTranslation=' + rightTranslation);
	rearTranslation = new SFVec3f  (width, 0, -depth);
	tracePrint ('rearTranslation=' + rearTranslation);
	leftTranslation = new SFVec3f  (0, 0, -width/2);
	tracePrint ('leftTranslation=' + leftTranslation);
	built = true;
	tracePrint ('built=' + built);
}
]]>
          </Script>
          <ROUTE fromField='rightTranslation' fromNode='LevelConstructionScript' toField='translation' toNode='RightWallTransform'/>
          <ROUTE fromField='rearTranslation' fromNode='LevelConstructionScript' toField='translation' toNode='RearWallTransform'/>
          <ROUTE fromField='leftTranslation' fromNode='LevelConstructionScript' toField='translation' toNode='LeftWallTransform'/>
          <Group DEF='LevelInitializeAfterChildrenReady'>
            <BooleanFilter DEF='LevelBuiltFilter'/>
            <BooleanFilter DEF='LevelBuiltNegation'/>
            <TimeSensor DEF='LevelRecalculateUntilBuilt' cycleInterval='0.1' loop='true'/>
            <ROUTE fromField='built' fromNode='LevelConstructionScript' toField='set_boolean' toNode='LevelBuiltFilter'/>
            <ROUTE fromField='inputTrue' fromNode='LevelBuiltFilter' toField='set_boolean' toNode='LevelBuiltNegation'/>
            <ROUTE fromField='inputNegate' fromNode='LevelBuiltNegation' toField='enabled' toNode='LevelRecalculateUntilBuilt'/>
            <ROUTE fromField='cycleTime' fromNode='LevelRecalculateUntilBuilt' toField='recheckUntilBuilt' toNode='LevelConstructionScript'/>
          </Group>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <ProtoDeclare appinfo='Collect prototypes for levels floors and walls to create a simple Building.' name='Building'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Identifying name of this Building.' name='name' type='SFString'/>
        <field accessType='initializeOnly' appinfo='Description info for this construction.' name='description' type='MFString'/>
        <field accessType='initializeOnly' appinfo='whether or not to display author assist tools such as coordinate axes measuring grids etc.' name='authorAssist' type='SFBool' value='true'/>
        <field accessType='initializeOnly' appinfo='compass direction in degrees of building X axis as seen when regarding front face of building pointing from left side to right side.' name='xHeading' type='SFFloat' value='0.0'/>
        <field accessType='outputOnly' appinfo='output rotation value calculated from xHeading as (0 1 0 xHeading * 2pi / 360)' name='orientation' type='SFRotation'/>
        <field accessType='initializeOnly' appinfo='example value: 120.30 E' name='latitude' type='SFString'/>
        <field accessType='initializeOnly' appinfo='example value: 20.45 N' name='longitude' type='SFString'/>
        <field accessType='initializeOnly' appinfo='contains array of Level nodes' name='levels' type='MFNode'>
          <ProtoInstance name='Level'/>
        </field>
        <field accessType='initializeOnly' appinfo='Geometry for Roof positioned above topmost Level' name='roof' type='SFNode'>
          <Group/>
        </field>
        <field accessType='initializeOnly' appinfo='height value for provided Roof geometry.' name='roofHeight' type='SFFloat' value='0'/>
        <field accessType='outputOnly' appinfo='Calculated width of horizontal side of Building aligned with local X axis.' name='width' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Calculated height of Building aligned with local Y axis.' name='height' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Calculated depth of horizontal thickness of Building aligned with local -Z axis.' name='depth' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='Indicate whether initialization complete.' name='built' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Group>
          <Switch DEF='AuthorAssist1' whichChoice='-1'>
            <Transform DEF='CoordinateAxesTransform'>
              <Inline DEF='CoordinateAxes' url='"../../X3dForWebAuthors/Chapter03Grouping/CoordinateAxes.x3d" "https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter03Grouping/CoordinateAxes.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/CoordinateAxes.x3d" "../../X3dForWebAuthors/Chapter03Grouping/CoordinateAxes.wrl" "https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter03Grouping/CoordinateAxes.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/CoordinateAxes.wrl"'/>
            </Transform>
          </Switch>
          <Group DEF='BuildingRoot'>
            <Transform DEF='LevelsRoot'>
              <Switch DEF='AuthorAssist2' whichChoice='-1'>
                <Transform DEF='AuthorAssistTransform2'>
                  <Inline url='"GridXY_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Basic/course/GridXY_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridXY_20x20Movable.wrl" "GridXY_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Basic/course/GridXY_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridXY_20x20Movable.x3d"'/>
                  <Inline url='"GridXZ_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Basic/course/GridXZ_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridXZ_20x20Movable.wrl" "GridXZ_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Basic/course/GridXZ_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridXZ_20x20Movable.x3d"'/>
                  <Inline url='"GridYZ_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Basic/course/GridYZ_20x20Movable.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridYZ_20x20Movable.wrl" "GridYZ_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Basic/course/GridYZ_20x20Movable.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/GridYZ_20x20Movable.x3d"'/>
                </Transform>
              </Switch>
            </Transform>
            <Script DEF='BuildingConstructionScript' directOutput='true'>
              <field accessType='initializeOnly' name='name' type='SFString'/>
              <field accessType='initializeOnly' name='description' type='MFString'/>
              <field accessType='initializeOnly' name='xHeading' type='SFFloat'/>
              <field accessType='outputOnly' name='orientation' type='SFRotation'/>
              <field accessType='initializeOnly' name='latitude' type='SFString'/>
              <field accessType='initializeOnly' name='longitude' type='SFString'/>
              <field accessType='initializeOnly' name='authorAssist' type='SFBool'/>
              <field accessType='initializeOnly' name='levels' type='MFNode'/>
              <field accessType='initializeOnly' name='roof' type='SFNode'/>
              <field accessType='initializeOnly' name='roofHeight' type='SFFloat'/>
              <field accessType='initializeOnly' name='LevelsRoot' type='SFNode'>
                <Transform USE='LevelsRoot'/>
              </field>
              <field accessType='outputOnly' name='width' type='SFFloat'/>
              <field accessType='outputOnly' name='height' type='SFFloat'/>
              <field accessType='outputOnly' name='depth' type='SFFloat'/>
              <field accessType='outputOnly' name='scale' type='SFVec3f'/>
              <field accessType='outputOnly' name='authorAssistChoice' type='SFInt32'/>
              <field accessType='initializeOnly' name='traceEnabled' type='SFBool' value='true'/>
              <field accessType='inputOnly' appinfo='New floor or wall initialization may be complete check and recalculate until built' name='recheckUntilBuilt' type='SFTime'/>
              <field accessType='outputOnly' name='built' type='SFBool'/>
              <IS>
                <connect nodeField='name' protoField='name'/>
                <connect nodeField='description' protoField='description'/>
                <connect nodeField='authorAssist' protoField='authorAssist'/>
                <connect nodeField='xHeading' protoField='xHeading'/>
                <connect nodeField='orientation' protoField='orientation'/>
                <connect nodeField='latitude' protoField='latitude'/>
                <connect nodeField='longitude' protoField='longitude'/>
                <connect nodeField='levels' protoField='levels'/>
                <connect nodeField='roof' protoField='roof'/>
                <connect nodeField='roofHeight' protoField='roofHeight'/>
                <connect nodeField='width' protoField='width'/>
                <connect nodeField='height' protoField='height'/>
                <connect nodeField='depth' protoField='depth'/>
                <connect nodeField='built' protoField='built'/>
              </IS>
              <![CDATA[
ecmascript:

var firstLoopWhileTrueCompleted;

function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[Building' + name + ']' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[Building' + name + ']' + outputString);
}
function initialize ()
{
	built = false;
	firstLoopWhileTrueCompleted = false;
	alwaysPrint ('initialize, built=' + built);
}
function recheckUntilBuilt (value)
{
	if      (built == true)
	{
		built = true; // resend to trigger cancellation event for TimeSensor
		if (firstLoopWhileTrueCompleted)
			alwaysPrint ('recheckUntilBuilt() continuous built=true indicates internal error');
		else firstLoopWhileTrueCompleted = true;
		return;  // done
	}
	tracePrint ('recheckUntilBuilt testing...');
	if      (built == true)			return;  // done
	for (i=0; i < levels.length; i++)
	{
		if (levels[i].built == false)	return;  // not yet ready
	}
	tracePrint ('recheckUntilBuilt ready!');

	alwaysPrint ('description=' + description);
	if (levels.length == 0)
	{
		alwaysPrint ('** warning, no levels found');
		return;
	}
	orientation = new SFRotation (0, 1, 0, -xHeading * 3.14159 / 180.0);
	LevelsRoot.rotation = orientation;
	tracePrint ('xHeading=' + xHeading + ' degrees,' + 'orientation=' + orientation);

	width  = 0;
	depth  = 0;
	incrementalHeight = 0;
	// first child of LevelsRoot is oriented AuthorAssist2 Switch
	for (i=0; i < levels.length; i++)
	{
		tracePrint ('level[' + i + ']');
		// compute max values for width, height, depth
		if (width  < levels[i].width)  width   = levels[i].width;
		if (depth  < levels[i].depth)  depth   = levels[i].depth;

		newTransform ='Transform {' +
		'translation 0' + incrementalHeight + ' 0' +
		'}';
		tracePrint ('newTransform=' + newTransform);
		newTransformNode = Browser.createVrmlFromString (newTransform); // returns MFNode
		// append newTransformNode to LevelsRoot.children
		LevelsRoot.children[i+1] = newTransformNode[0];
		// append current Level to current newTransformNode
		newTransformNode[0].children[0] = levels[i];

		incrementalHeight += levels[i].height;
		tracePrint ('incrementalHeight=' + incrementalHeight);
	}
	childCount = levels.length + 1;
	if (roof)
	{
		tracePrint ('roof');
		newTransform ='Transform {' +
		'translation 0' + incrementalHeight + ' 0' +
		'}';
		tracePrint ('newTransform=' + newTransform);
		newTransformNode = Browser.createVrmlFromString (newTransform); // returns MFNode
		// append newTransformNode to LevelsRoot.children
		LevelsRoot.children[childCount] = newTransformNode[0];
		childCount++;
		// append roof to current newTransformNode
		newTransformNode[0].children[0] = roof;
		incrementalHeight += roofHeight;
	}
	else alwaysPrint ('** warning, no roof found');

	height = incrementalHeight;
	tracePrint ('(width, height, depth)=(' + width + ',' + height + ',' +  depth + ')');
	maxDimension = width;
	if (maxDimension < height) maxDimension = height;
	if (maxDimension < depth)  maxDimension = depth;
	scale = new SFVec3f (maxDimension * 1.1, maxDimension * 1.1, maxDimension * 1.1);

	newView ='Viewpoint {' +
	'description \"' + name + ' (' + latitude + ',' + longitude + ')\"' +
	'position' + (width/2) + ' ' + (height*2.0/3.0) + ' ' + (depth*3) + ' ' +
	'}';
	tracePrint ('newView=' + newView);
	newViewNode = Browser.createVrmlFromString (newView); // returns MFNode
	// append newViewNode to LevelsRoot.children
	LevelsRoot.children[childCount] = newViewNode[0];
	childCount++;

	newView ='Viewpoint {' +
	'description \"' + name + ' (above)\"' +
	'position' + (width/2) + ' ' + (height*3.0) + ' ' + (-depth/2) + ' ' +
	'orientation 1 0 0 -1.57' +
	'}';
	tracePrint ('newView=' + newView);
	newViewNode = Browser.createVrmlFromString (newView); // returns MFNode
	// append newViewNode to LevelsRoot.children
	LevelsRoot.children[childCount] = newViewNode[0];
	childCount++;

	if (authorAssist)
	{
		tracePrint ('authorAssist');
		authorAssistChoice = 0;
	}
	else	authorAssistChoice = -1;

	tracePrint ('LevelsRoot childCount=' + childCount + ' (Switch + # levels + [roof] + Viewpoint*2)');
	built = true;
	tracePrint ('built=' + built);
}
]]>
            </Script>
            <ROUTE fromField='authorAssistChoice' fromNode='BuildingConstructionScript' toField='whichChoice' toNode='AuthorAssist1'/>
            <ROUTE fromField='authorAssistChoice' fromNode='BuildingConstructionScript' toField='whichChoice' toNode='AuthorAssist2'/>
            <ROUTE fromField='scale' fromNode='BuildingConstructionScript' toField='scale' toNode='CoordinateAxesTransform'/>
            <Group DEF='BuildingInitializeAfterChildrenReady'>
              <BooleanFilter DEF='BuildingBuiltFilter'/>
              <BooleanFilter DEF='BuildingBuiltNegation'/>
              <TimeSensor DEF='BuildingRecalculateUntilBuilt' cycleInterval='0.1' loop='true'/>
              <ROUTE fromField='built' fromNode='BuildingConstructionScript' toField='set_boolean' toNode='BuildingBuiltFilter'/>
              <ROUTE fromField='inputTrue' fromNode='BuildingBuiltFilter' toField='set_boolean' toNode='BuildingBuiltNegation'/>
              <ROUTE fromField='inputNegate' fromNode='BuildingBuiltNegation' toField='enabled' toNode='BuildingRecalculateUntilBuilt'/>
              <ROUTE fromField='cycleTime' fromNode='BuildingRecalculateUntilBuilt' toField='recheckUntilBuilt' toNode='BuildingConstructionScript'/>
            </Group>
          </Group>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <!-- ============================ -->
    <Viewpoint description='SimpleBuildingConstructionPrototypes' position='0 0 12'/>
    <Background groundAngle='1.57' groundColor='0.6 0.9 0.6 0.6 0.9 0.6' skyColor='0.6 0.6 0.9'/>
    <Anchor description='SimpleBuildingConstructionExample' parameter='"target=_blank"' url='"SimpleBuildingConstructionExample.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Buildings/UHRB/SimpleBuildingConstructionExample.wrl" "SimpleBuildingConstructionExample.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Buildings/UHRB/SimpleBuildingConstructionExample.x3d"'>
      <Shape>
        <Text string='"SimpleBuildingConstructionPrototypes" "is a prototype definition file" "" "Click this text to see" "SimpleBuildingConstructionExample"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"'/>
        </Text>
        <Appearance>
          <Material diffuseColor='0.2 0.2 0.8'/>
        </Appearance>
      </Shape>
    </Anchor>
  </Scene>
</X3D>