Return to Appendix B.

NVIS.CPP
// Matt Streeter
// 2/27/00
// NVIS.CPP
// Implementation of class TNVisWindow; window class for visualization of a
// single neural network.
// Copyright Matt Streeter, 2000.  All rights reserved

#include "nvis.h"
#include "ids.h"

#include <math.h>
#include <stdio.h>
#include "eswin.rh"

DEFINE_RESPONSE_TABLE1(TNVisWindow, TESWindow)
  EV_WM_SIZE,
  EV_CHILD_NOTIFY_ALL_CODES(ID_SAMPLE_SCROLL,EvSampleScroll),
  EV_CHILD_NOTIFY_ALL_CODES(ID_SAMPLE_EDIT,EvSampleEdit),
  EV_WM_LBUTTONDOWN,
  EV_WM_LBUTTONUP,
  EV_WM_MOUSEMOVE,
  EV_COMMAND(CM_FILE_LOAD,CmFileLoad),
  EV_COMMAND(CM_FILE_SAVE,CmFileSave),
  EV_COMMAND(CM_FILE_SAVE_AS,CmFileSaveAs),
  EV_COMMAND(CM_FILE_LOADTESTSET,CmFileLoadTestSet),
  EV_COMMAND_ENABLE(CM_FILE_SAVE,CeFileSave),
  EV_COMMAND(CM_NETWORK_EVAL,CmNetworkEval),
  EV_COMMAND(CM_NETWORK_VIEW_ACTIVATIONS,CmNetworkViewActivations),
  EV_COMMAND(CM_NETWORK_BACKPROP,CmNetworkBackProp),
  EV_COMMAND(CM_VIEW_VOLATILITY,CmViewVolatility),
  EV_COMMAND_ENABLE(CM_VIEW_VOLATILITY,CeViewVolatility),
END_RESPONSE_TABLE;

// The following four #defines determine, in pixels, the left, right, top,
// and bottom borders, respectively, between window contents and the window
// edge.
#define WINDOW_LBORDER 20
#define WINDOW_RBORDER 20
#define WINDOW_TBORDER 5
#define WINDOW_BBORDER 0

// Height in pixels of horizontal scroll bar used to scroll through samples.
#define SCROLLBAR_HEIGHT 25

// Height and width of sample edit control.
#define SAMPLE_EDIT_HEIGHT 25
#define SAMPLE_EDIT_WIDTH 50

// Height and width of "Sample" text static.
#define SAMPLE_STATIC_HEIGHT 25
#define SAMPLE_STATIC_WIDTH 75

// Horizontal space (in pixels) between "Sample" text static and edit control.
#define SAMPLE_STATIC_SPACING 25

// Font face and height for node information.
#define NODE_FONT_FACE "Times New Roman"
#define NODE_FONT_HEIGHT 20

// Activations are multiplied by 'SIGMOID_SCALE_FACTOR' before being passed
// through a sigmoid function to determine the diameter of the circle used
// to display the activation.
#define SIGMOID_SCALE_FACTOR 10.0f

// thickness of weight lines
#define WEIGHT_THICKNESS 2

// thickness of sigma lines
#define SIGMA_THICKNESS 2

// Set to 1 to show sigma (volatility) information by default, 0 otherwise.
#define SHOW_SIGMAS_DEFAULT 1

TColor TNVisWindow::mGrayLineColor(64,64,64);
TColor TNVisWindow::mRingColor(0,0,0);
TColor TNVisWindow::mActivityColor(255,255,255);

TNVisWindow::TNVisWindow(TWindow *pParent,Network *pNetwork,
	Chromosome *pChromosome,Parameter *pParam)
	:TESWindow(pParent,0)
{
	Attr.Style|=WS_VISIBLE|WS_OVERLAPPEDWINDOW;
	// tbd: account correctly for parent coords
	Attr.X=475+(pParent?pParent->Attr.X+3:0);
	Attr.Y=200+(pParent?pParent->Attr.Y+25:0);
	Attr.W=275;
	Attr.H=325;

	mParam=*(pParam->Copy());
	mChromosome.InitChromosome(pChromosome->GetLength());
	mChromosome=*pChromosome;
	miWeights=mParam.GetNParams();
	Decode(mChromosome,mParam);
	mpWeights=new double[miWeights];
	memcpy(mpWeights,mParam.GetParams(),miWeights*sizeof(double));
	mdMaxWeight=mParam.GetUpper(0);
	mNetwork=*(pNetwork->Copy());
	miSample=0;
	miSelectedNode=-1;
	miSelectedWeight=-1;
   miSelectedWeightSign=0;
	mpFilename=0;
	mbShowSigmas=SHOW_SIGMAS_DEFAULT;
	mpBackpropWin=0;

	mpNodeCenterPoints=new TPoint[mNetwork.GetNumNodes()];
	mpWeightEndPoints=new TDrawnWeight[miWeights];

	mpSampleStatic=new TESStatic(this,ID_SAMPLE_STATIC,"Sample:",0,0,
		SAMPLE_STATIC_WIDTH,SAMPLE_STATIC_HEIGHT);
	mpSampleEdit=new TEdit(this,ID_SAMPLE_EDIT,"0",
		SAMPLE_STATIC_WIDTH+SAMPLE_STATIC_SPACING,0,
		SAMPLE_EDIT_WIDTH,SAMPLE_EDIT_HEIGHT);

	// correct dimensions will be set up by EvSize()
	mpHScroll=new TScrollBar(this,ID_SAMPLE_SCROLL,0,0,0,0,TRUE);

	mNetwork.RunSample(mpWeights,miWeights,miSample);

	UpdateCaption();
	AssignMenu(NETWORK_FILE_MENU);
}

TNVisWindow::~TNVisWindow()
{
	delete(mpWeights);
}

void TNVisWindow::SetupWindow()
{
	TFrameWindow::SetupWindow();
	mpHScroll->SetRange(0,mNetwork.GetNumSamples()-1);
}

bool TNVisWindow::IdleAction(long lIdleCount)
{
	ChildrenIdleAction(lIdleCount);
	return(true);
}

void TNVisWindow::BackProp()
{
	mNetwork.BackProp(mpWeights,miWeights);

	// enforce parameter bounds
	for(int m=0;m<miWeights;m++)
	{
		if(mpWeights[m]>mParam.GetUpper(m))
			mpWeights[m]=mParam.GetUpper(m);
		if(mpWeights[m]<mParam.GetLower(m))
			mpWeights[m]=mParam.GetLower(m);
	}

	// calculate activity levels for this sample
	mNetwork.RunSample(mpWeights,miWeights,miSample);

	// redraw window
	Invalidate(TRUE);
}

double TNVisWindow::GetFitness()
{
#if(!ATTRIBUTE_EVAL)
	double dFitness=sqrt(mNetwork.NetEval(mpWeights,miWeights));
#else
	double dFitness=100.0f-100.0f*mNetwork.NetEval(mpWeights,miWeights);
#endif

	// we must run the sample we're on for the activity levels to still be right
	mNetwork.RunSample(mpWeights,miWeights,miSample);

	return(dFitness);
}

// the ratio of vertical space between circles to the diameter of the circles
#define VSPACE_RATIO 2.0
// the same for horizontal space
#define HSPACE_RATIO 2.0

#define RING_THICKNESS 2.0

void TNVisWindow::Paint(TDC& dc, bool, TRect&)
{
int n;
TRect DrawRect;

	GetClientRect(DrawRect);
	DrawRect.bottom-=SCROLLBAR_HEIGHT;
	if(DrawRect.bottom<DrawRect.top)
		return;

	DrawRect.top+=SAMPLE_EDIT_HEIGHT>SAMPLE_STATIC_HEIGHT?
		SAMPLE_EDIT_HEIGHT:SAMPLE_STATIC_HEIGHT;
	DrawRect.bottom-=NODE_FONT_HEIGHT*2;
	if(DrawRect.bottom<DrawRect.top)
		return;

	if(miWeights!=mNetwork.NumWeights())
		return;

	if(miSample==mNetwork.GetNumSamples())
		miSample=0;

	DrawRect.left+=WINDOW_LBORDER;
	DrawRect.top+=WINDOW_TBORDER;
	DrawRect.right-=WINDOW_RBORDER;
	DrawRect.bottom-=WINDOW_BBORDER;

	// find maximum sigma -- we will scale relative to this
	double dMaxSigma=0.0f;
	for(n=0;n<miWeights;n++)
	{
		double dSigma=fabs(mChromosome.GetGene(n+miWeights));
		if(dSigma>dMaxSigma)
      	dMaxSigma=dSigma;
	}

	// for now let link height be 2*(circle height)
	float fCircleHeight=(float)DrawRect.Height()/
		(mNetwork.GetLayers()+VSPACE_RATIO*(mNetwork.GetLayers()-1));

	// find maximum layer size
	int iMaxLayerSize=0;
	for(n=0;n<mNetwork.GetLayers();n++)
	{
		if(mNetwork.GetLayerSize(n)>iMaxLayerSize)
			iMaxLayerSize=mNetwork.GetLayerSize(n);
	}

	// we also let the horizontal space between the the circles be 2 times
	// the circles' width
	float fCircleWidth=(float)DrawRect.Width()/
		(iMaxLayerSize+HSPACE_RATIO*(iMaxLayerSize-1));

	float fCircleDiameter=fCircleHeight<fCircleWidth?
		fCircleHeight:fCircleWidth;
	mfNodeRadius=fCircleDiameter/2.0;

	float VSpacing=((float)DrawRect.Height()-fCircleDiameter)/
		(float)(mNetwork.GetLayers()-1);
	float HSpacing=(iMaxLayerSize!=1?((float)DrawRect.Width()-fCircleDiameter)/
		(float)(iMaxLayerSize-1):0);

	float XCursor,YCursor=(float)DrawRect.top+mfNodeRadius;
	float XCenter=(float)DrawRect.left+(float)DrawRect.Width()/2.0;

	int iWeightOffset=0;

	TPen RingPen(mRingColor,RING_THICKNESS);
	TPen ActivityPen(mActivityColor,1);
	TBrush ActivityBrush(mActivityColor);

	int iNodesDrawn=0;
	for(int iLayer=0;iLayer<mNetwork.GetLayers();iLayer++)
	{
		XCursor=XCenter-HSpacing*(float)(mNetwork.GetLayerSize(iLayer)-1)/2.0;

		for(int iNode=0;iNode<mNetwork.GetLayerSize(iLayer);iNode++)
		{
			// draw weights from this circle to circles in next layer
			if(iLayer<mNetwork.GetLayers()-1)
			{
				float WXCursor=XCenter-HSpacing*
					(float)(mNetwork.GetLayerSize(iLayer+1)-1)/2.0;
				TPoint Center(XCursor,YCursor);
				for(int m=0;m<mNetwork.GetLayerSize(iLayer+1);m++)
				{
					// weight indexing is kind of tricky
					int iWeight=iWeightOffset+m*(mNetwork.GetLayerSize(iLayer))
						+iNode;

					float fHypotenuse=sqrt(pow(XCursor-WXCursor,2)+pow(VSpacing,2));
					float fXRadius=mfNodeRadius*(WXCursor-XCursor)/fHypotenuse;
					float fYRadius=mfNodeRadius*VSpacing/fHypotenuse;
					TPoint P1(XCursor+fXRadius,YCursor+fYRadius);
					TPoint P2(WXCursor-fXRadius,YCursor+VSpacing-fYRadius);

					TPoint PCenter=DrawWeight(dc,P1,P2,
						mdMaxWeight?mpWeights[iWeight]/mdMaxWeight:0,
						dMaxSigma?
							fabs(mChromosome.GetGene(iWeight+miWeights))/dMaxSigma:0,
							WEIGHT_THICKNESS,mfNodeRadius*2.0);

					// record weight end-point for future use
					mpWeightEndPoints[iWeight].P1=P1;
					mpWeightEndPoints[iWeight].P2=P2;
					mpWeightEndPoints[iWeight].PCenter=PCenter;

					WXCursor+=HSpacing;
				}
			}

			// draw black ring
			dc.SelectObject(mBackgroundFillBrush);
			dc.SelectObject(RingPen);
			TPoint OuterX1Y1(XCursor-mfNodeRadius,YCursor-mfNodeRadius);
			TPoint OuterX2Y2(XCursor+mfNodeRadius,YCursor+mfNodeRadius);
			dc.Ellipse(OuterX1Y1,OuterX2Y2);

			// draw inner white circle
			dc.SelectObject(ActivityBrush);
			dc.SelectObject(ActivityPen);
			double dActiv=mNetwork.GetActivation(iLayer,iNode);
			float fThisCircleRadius=(mfNodeRadius-RING_THICKNESS)
				*Sigmoid(dActiv*SIGMOID_SCALE_FACTOR);
			TPoint InnerX1Y1(XCursor-fThisCircleRadius,YCursor-fThisCircleRadius);
			TPoint InnerX2Y2(XCursor+fThisCircleRadius,YCursor+fThisCircleRadius);
			dc.Ellipse(InnerX1Y1,InnerX2Y2);

			// record center point for future use
			mpNodeCenterPoints[iNodesDrawn].x=XCursor;
			mpNodeCenterPoints[iNodesDrawn].y=YCursor;
			iNodesDrawn++;

			XCursor+=HSpacing;
		}

		if(iLayer<mNetwork.GetLayers()-1)
		{
			iWeightOffset+=mNetwork.GetLayerSize(iLayer)
					*(mNetwork.GetLayerSize(iLayer+1));
		}

		YCursor+=VSpacing;
	}
	if(miSelectedNode!=-1)
	{
		DrawHighlight(miSelectedNode,1);
		DrawNodeInfo();
	}
}

int TNVisWindow::FindNode(TPoint& point)
{
int n,iNodes;

	iNodes=mNetwork.GetNumNodes();
	for(n=0;n<iNodes;n++)
	{
		if(pow(point.x-mpNodeCenterPoints[n].x,2)
			+pow(point.y-mpNodeCenterPoints[n].y,2)<pow(mfNodeRadius,2))
		{
			return(n);
		}
	}
	return(-1);
}

int TNVisWindow::FindWeight(TPoint& point)
{
	for(int n=0;n<miWeights;n++)
	{
		if(pow(point.x-mpWeightEndPoints[n].PCenter.x,2)
			+pow(point.y-mpWeightEndPoints[n].PCenter.y,2)
			<pow(mfNodeRadius*2.0,2))
		{
			return(n);
		}
	}
	return(-1);

}

void TNVisWindow::DrawHighlight(int iNode,byte bHighlight)
{
int iRGB=bHighlight?255:0;
LOGBRUSH HollowLogBrush={BS_HOLLOW,0,0};

	TPen HighlightPen(TColor(iRGB,iRGB,iRGB),1);
	TClientDC dc(*this);
	dc.SelectObject(HighlightPen);
	TPoint OuterX1Y1(mpNodeCenterPoints[iNode].x-mfNodeRadius,
		mpNodeCenterPoints[iNode].y-mfNodeRadius);
	TPoint OuterX2Y2(mpNodeCenterPoints[iNode].x+mfNodeRadius,
		mpNodeCenterPoints[iNode].y+mfNodeRadius);
	TBrush HollowBrush(&HollowLogBrush);
	dc.SelectObject(HollowBrush);
	dc.Ellipse(OuterX1Y1,OuterX2Y2);
}

void TNVisWindow::DrawNodeInfo()
{
	if(miSelectedNode==-1)
		return;

	TClientDC dc(*this);
	TRect ClientRect;
	GetClientRect(ClientRect);

	TRect DrawRect=ClientRect;
	DrawRect.bottom-=SCROLLBAR_HEIGHT;
	DrawRect.top=DrawRect.bottom-NODE_FONT_HEIGHT*2;
	dc.FillRect(DrawRect,mBackgroundFillBrush);

	TFont NodeFont(NODE_FONT_FACE,NODE_FONT_HEIGHT);
	dc.SelectObject(NodeFont);
	dc.SetBkColor(mBackgroundColor);
	dc.SetTextColor(mTextColor);
	TPoint TextPos(0,ClientRect.bottom-SCROLLBAR_HEIGHT-NODE_FONT_HEIGHT*2);
	dc.TextOut(TextPos,"Input:",6);

	TSize Size;
	dc.GetTextExtent("Input: ",7,Size);
	TextPos.x+=Size.cx;
	char temp[200];
	sprintf(temp,"%f",mNetwork.GetInput(miSelectedNode,miSample,mpWeights));
	dc.TextOut(TextPos,temp,strlen(temp));

	TextPos.x=0;
	TextPos.y+=NODE_FONT_HEIGHT;
	dc.TextOut(TextPos,"Activation:",11);

	dc.GetTextExtent("Activation: ",12,Size);
	TextPos.x+=Size.cx;
	sprintf(temp,"%f",mNetwork.GetActivation(miSelectedNode));
	dc.TextOut(TextPos,temp,strlen(temp));
}

TPoint TNVisWindow::DrawWeight(TDC& dc,TPoint& P1,TPoint& P2,
	double dNormWeight,double dNormSigma,double dMinSigmaLen,
	double dMaxSigmaLen)
{
	if(dNormWeight<-1.0)
		dNormWeight=-1.0;
	if(dNormWeight>1.0)
		dNormWeight=1.0;
	if(dNormSigma<-1.0)
		dNormSigma=-1.0;
	if(dNormSigma>1.0)
		dNormSigma=1.0;

	TPen WeightPen(WeightToColor(dNormWeight),WEIGHT_THICKNESS);
	dc.SelectObject(WeightPen);

	dc.MoveTo(P1);
	TPoint PC(P1.x+fabs(dNormWeight)*(P2.x-P1.x),
		P1.y+fabs(dNormWeight)*(P2.y-P1.y));
	dc.LineTo(PC);
	TPen GrayPen(mGrayLineColor,WEIGHT_THICKNESS);
	dc.SelectObject(GrayPen);
	dc.MoveTo(PC);
	dc.LineTo(P2);

	if(!mbShowSigmas)
		return(PC);

	double dMaxDeltaLen=double(dMaxSigmaLen-dMinSigmaLen);
	double dSigmaLen=dNormSigma*dMaxDeltaLen;
	// tbd: pass in
	double dHypotenuse=sqrt(pow(P1.x-P2.x,2)+pow(P1.y-P2.y,2));
	double dXRatio=double(P2.y-P1.y)/dHypotenuse;
	double dYRatio=double(P1.x-P2.x)/dHypotenuse;
	double dDeltaX=dSigmaLen*dXRatio/2.0;
	double dDeltaY=dSigmaLen*dYRatio/2.0;
	double dMaxDeltaX=dMaxDeltaLen*dXRatio/2.0;
	double dMaxDeltaY=dMaxDeltaLen*dYRatio/2.0;

	// draw gray line in back
	TPoint PG1(PC.x-dMaxDeltaX,PC.y-dMaxDeltaY);
	TPoint PG2(PC.x+dMaxDeltaX,PC.y+dMaxDeltaY);
	dc.MoveTo(PG1);
	dc.LineTo(PG2);

	TPoint PS1(PC.x-dDeltaX,PC.y-dDeltaY);
	TPoint PS2(PC.x+dDeltaX,PC.y+dDeltaY);
	TPen SigmaPen(SigmaToColor(dNormSigma),SIGMA_THICKNESS);
	dc.SelectObject(SigmaPen);
	dc.MoveTo(PS1);
	dc.LineTo(PS2);

	return(PC);
}

void TNVisWindow::EvLButtonDown(uint, TPoint& point)
{
int iNode;

	iNode=FindNode(point);

	if(iNode!=-1)
	{
		if(iNode==miSelectedNode)
		{
			DrawHighlight(iNode,0);
			miSelectedNode=-1;
			return;
		}

		if(miSelectedNode!=-1)
			DrawHighlight(miSelectedNode,0);
		DrawHighlight(iNode,1);
		miSelectedNode=iNode;

		DrawNodeInfo();
		return;
	}
	miSelectedWeight=FindWeight(point);
	if(miSelectedWeight!=-1)
	{
		if(mpWeights[miSelectedWeight]>0)
			miSelectedWeightSign=1;
		else
      	miSelectedWeightSign=-1;
	}
}

void TNVisWindow::EvLButtonUp(uint, TPoint&)
{
	miSelectedWeight=-1;
}

void TNVisWindow::EvMouseMove(uint modKeys, TPoint& point)
{
	if(miSelectedWeight!=-1)
	{
		int iEffectiveY=point.y;

		if(iEffectiveY<mpWeightEndPoints[miSelectedWeight].P1.y)
			iEffectiveY=mpWeightEndPoints[miSelectedWeight].P1.y;
		if(iEffectiveY>mpWeightEndPoints[miSelectedWeight].P2.y)
			iEffectiveY=mpWeightEndPoints[miSelectedWeight].P2.y;

		mpWeights[miSelectedWeight]=float(iEffectiveY
			-mpWeightEndPoints[miSelectedWeight].P1.y)*mdMaxWeight/
				(mpWeightEndPoints[miSelectedWeight].P2.y
					-mpWeightEndPoints[miSelectedWeight].P1.y)
				*float(miSelectedWeightSign);

		double dFitness=GetFitness();
		char temp[200];
		sprintf(temp,"%lf",dFitness);
#if(ATTRIBUTE_EVAL)
		strcat(temp,"%");
#endif
      SetCaption(temp);

		// tbd: just redraw network
		Invalidate(TRUE);
	}
	TESWindow::EvMouseMove(modKeys,point);
}

void TNVisWindow::EvSampleEdit(UINT /*code*/)
{
char temp[200];

	mpSampleEdit->GetText(temp,200);
	miSample=atoi(temp);
	if(miSample<0||miSample>=mNetwork.GetNumSamples())
	{
		miSample=miSample<0?0:mNetwork.GetNumSamples()-1;
		UpdateSampleEdit();
	}
	mNetwork.RunSample(mpWeights,miWeights,miSample);
	mpHScroll->SetPosition(miSample);
	Invalidate(FALSE);
}

void TNVisWindow::EvSampleScroll(UINT /*code*/)
{
	miSample=mpHScroll->GetPosition();
	mNetwork.RunSample(mpWeights,miWeights,miSample);
	UpdateSampleEdit();
	Invalidate(FALSE);
}

void TNVisWindow::EvSize(UINT SizeType, TSize& Size)
{
	TWindow::EvSize(SizeType, Size);
	if(SizeType != SIZEICONIC)
	{
		// reposition scroll bar
		TRect ClientRect;
		GetClientRect(ClientRect);
		mpHScroll->MoveWindow(0,
			ClientRect.bottom-SCROLLBAR_HEIGHT-ClientRect.top,
			ClientRect.Width(),SCROLLBAR_HEIGHT);
		Invalidate(TRUE);
	}
}

void TNVisWindow::CmFileLoad()
{
TOpenSaveDialog::TData FilenameData(OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST,
	"Networks (*.net)|*.net|All Files (*.*)|*.*|",0,"","*");

	if(TFileOpenDialog(this, FilenameData, 0, "Load Network").Execute()
		== IDOK)
	{
		if(Load(FilenameData.FileName)<0)
			MessageBox("File load failed!","Error!");
	}
}

void TNVisWindow::CmFileSave()
{
	if(mpFilename)
   	SaveAs(mpFilename);
}

void TNVisWindow::CmFileSaveAs()
{
TOpenSaveDialog::TData FilenameData(OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST,
	"Networks (*.net)|*.net|All Files (*.*)|*.*|",0,"","*");

	if(TFileSaveDialog(this, FilenameData, 0, "Save Network").Execute()
		== IDOK)
	{
		SaveAs(FilenameData.FileName);
	}
}

void TNVisWindow::CmFileLoadTestSet()
{
TOpenSaveDialog::TData FilenameData(OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST,
	"Training Sets (*.ts)|*.ts|All Files (*.*)|*.*|",0,"","*");

	if(TFileOpenDialog(this, FilenameData, 0, "Open Training File").Execute()
		== IDOK)
	{
   	// tbd: check for incompatible training set
		if(mNetwork.LoadTrainingSet(FilenameData.FileName)<0)
		{
			MessageBox("File load failed!","Error!");
		}
	}
}

void TNVisWindow::CeFileSave(TCommandEnabler& ce)
{
	ce.Enable(mpFilename?1:0);
}

void TNVisWindow::CeViewVolatility(TCommandEnabler& ce)
{
	ce.SetCheck(mbShowSigmas);
}

void TNVisWindow::CmNetworkEval()
{
char pMessage[200];

	sprintf(pMessage,"Fitness: %lf",GetFitness());
#if(ATTRIBUTE_EVAL)
	strcat(pMessage,"%");
#endif
	MessageBox(pMessage,"Fitness");
}

void TNVisWindow::CmNetworkViewActivations()
{
char pMessage[500];
int n;

	pMessage[0]='\0';
	for(n=0;n<mNetwork.GetLayers();n++)
	{
		strcat(pMessage,mNetwork.GetActivationName(n));
		strcat(pMessage,"\n");
	}
	MessageBox(pMessage,"Activations");
}

void TNVisWindow::CmNetworkBackProp()
{
	if(!mpBackpropWin)
	{
		mpBackpropWin=new TBackpropWindow(this,this,&mpBackpropWin);
		if(mpBackpropWin)
			mpBackpropWin->Create();
	}
}

void TNVisWindow::CmViewVolatility()
{
	mbShowSigmas=!mbShowSigmas;
	Invalidate(TRUE);
}

int TNVisWindow::SaveAs(char *pFile)
{
	ofstream outfile(pFile);
	outfile << mNetwork;
	outfile << "#Chromosome#\n";
	outfile << mChromosome;
	outfile << (*((ParamInfo*)(&mParam)));
	mpFilename=pFile;
	UpdateCaption();
	return(0);
}

int TNVisWindow::Load(char *pFile)
{
char line[256];

	mpFilename=pFile;
	ifstream infile(pFile);
	infile >> mNetwork;
	if(!mNetwork.GetLoadStatus())
		return(-1);
	infile >> line;
	infile >> mChromosome;
	infile >> (*((ParamInfo*)(&mParam)));
	mParam.SetNumParameters(mParam.GetNParams());
                 
	Decode(mChromosome,mParam);
	miWeights=mNetwork.NumWeights();

	delete(mpWeights);
	mpWeights=new double[miWeights];
	memcpy(mpWeights,mParam.GetParams(),miWeights*sizeof(double));

	delete(mpNodeCenterPoints);
	delete(mpWeightEndPoints);
	mpNodeCenterPoints=new TPoint[mNetwork.GetNumNodes()];
	mpWeightEndPoints=new TDrawnWeight[miWeights];

	mNetwork.RunSample(mpWeights,miWeights,miSample);
	UpdateCaption();
	Invalidate(TRUE);
	return(0);
}

void TNVisWindow::UpdateSampleEdit()
{
char temp[200];

	itoa(miSample,temp,10);
	mpSampleEdit->SetText(temp);
}

void TNVisWindow::UpdateCaption()
{
char pTitle[200];

	strcpy(pTitle,"Network Editor - ");

	if(mpFilename)
	{
	char *pFilename=&(mpFilename[strlen(mpFilename)]);

		while(pFilename>=mpFilename && pFilename[0]!='/' && pFilename[0]!='\\')
			pFilename--;
		pFilename++;
		int iStrlen=strlen(pTitle);
		strcat(pTitle,pFilename);
      strlwr(&(pTitle[iStrlen]));
	}
	else
	{
		mChromosome.GetName(&(pTitle[strlen(pTitle)]));
	}
	SetCaption(pTitle);
}

TColor WeightToColor(double dNormWeight)
{
	float fIntensity=96.0+159.0*fabs(dNormWeight);
	if(dNormWeight<0)
		return(TColor(fIntensity,0,0));
	else
		return(TColor(0,fIntensity,fIntensity));
}

TColor SigmaToColor(double dNormSigma)
{
	float fIntensity=96.0+159.0*dNormSigma;
	return(TColor(0,fIntensity,0));
}

Return to Appendix B.