home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
src
/
mactech
/
volume18_2002
/
18.05.sit
/
18.05
/
PC
/
SxChartApp.cp
< prev
next >
Wrap
Text File
|
2002-01-30
|
15KB
|
586 lines
//////////////////////////////////////////////////////////////////////
//
// S*xChart (MacTech Programmer's Challenge, February 2002)
// Written by Allen Stenger, January 2002
//
// This file includes portions of:
// CBasicApp.cp, ©1994-2001 Metrowerks Inc. All rights reserved.
// Project is based on the Metrowerks Basic Application stationery.
//
// We assume the files are in the same folder as the application.
// We assume the files are numbered starting at 00.
//
// Our strategy:
//
// The challenge is to produce a planar embedding (or almost planar)
// of a given graph. We determine the connected components of the
// given graph and apply a "spring embedder" algorithm to each
// component (the spring embedder doesn't work on disconnected
// graphs). Then we shift each component so they are stacked
// vertically, not intersecting each other. More details of the
// spring embedder are given with its code.
//
// To display the graph, we create a Picture in memory and draw
// the Picture into the display window.
//
//////////////////////////////////////////////////////////////////////
#include "SxChartApp.h"
#include "CGraphEmbedder.h"
#include "CGraphWindow.h"
#include <LGrowZone.h>
#include <PP_Messages.h>
#include <PP_Resources.h>
#include <UDrawingState.h>
#include <UMemoryMgr.h>
#include <URegistrar.h>
#include <LActiveScroller.h>
#include <LWindow.h>
#include <LCaption.h>
#include <ctime>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
// ===========================================================================
// • main
// ===========================================================================
int main()
{
// Set Debugging options
SetDebugThrow_(debugAction_Alert);
SetDebugSignal_(debugAction_Alert);
// Initialize Memory Manager. Parameter is the number of
// master pointer blocks to allocate
InitializeHeap(3);
// Initialize standard Toolbox managers
UQDGlobals::InitializeToolbox();
// Install a GrowZone to catch low-memory situations
new LGrowZone(20000);
// Create the application object and run
SxChartApp theApp;
theApp.Run();
return 0;
}
// ---------------------------------------------------------------------------
// • SxChartApp [public]
// ---------------------------------------------------------------------------
// Application object constructor
SxChartApp::SxChartApp()
{
RegisterClasses();
}
// ---------------------------------------------------------------------------
// • ~SxChartApp [public, virtual]
// ---------------------------------------------------------------------------
// Application object destructor
SxChartApp::~SxChartApp()
{
// Nothing
}
// ---------------------------------------------------------------------------
// • ObeyCommand [public, virtual]
// ---------------------------------------------------------------------------
// Respond to Commands. Returns true if the Command was handled, false if not.
Boolean
SxChartApp::ObeyCommand(
CommandT inCommand,
void* ioParam)
{
Boolean cmdHandled = true; // Assume we'll handle the command
switch (inCommand) {
case cmd_RunAllTests:
{
UCursor::SetWatch(); // a slow operation - show watch
RunAllTests();
}
break;
case cmd_RunOneTest:
{
int runOneNumber;
if (AskForTestNumber(&runOneNumber))
{
UCursor::SetWatch(); // a slow operation - show watch
RunAndLog(runOneNumber, runOneNumber);
}
}
break;
case cmd_DrawGraph:
{
int drawOneNumber;
if (AskForTestNumber(&drawOneNumber))
{
DrawGraph(drawOneNumber);
}
}
break;
default: {
cmdHandled = LApplication::ObeyCommand(inCommand, ioParam);
break;
}
}
return cmdHandled;
}
// ---------------------------------------------------------------------------
// • FindCommandStatus [public, virtual]
// ---------------------------------------------------------------------------
// Determine the status of a Command for the purposes of menu updating.
void
SxChartApp::FindCommandStatus(
CommandT inCommand,
Boolean& outEnabled,
Boolean& outUsesMark,
UInt16& outMark,
Str255 outName)
{
switch (inCommand) {
case cmd_RunAllTests:
{
outEnabled = true;
}
break;
case cmd_RunOneTest:
{
outEnabled = true;
}
break;
case cmd_DrawGraph:
{
outEnabled = true;
}
break;
default: {
LApplication::FindCommandStatus(inCommand, outEnabled,
outUsesMark, outMark, outName);
break;
}
}
}
// ---------------------------------------------------------------------------
// • RegisterClasses [protected]
// ---------------------------------------------------------------------------
// To reduce clutter within the Application object's constructor, class
// registrations appear here in this separate function for ease of use.
void
SxChartApp::RegisterClasses()
{
RegisterClass_(LWindow);
RegisterClass_(LCaption);
RegisterClass_(LDialogBox);
RegisterClass_(LEditField);
RegisterClass_(LStdButton);
RegisterClass_(LActiveScroller);
RegisterClass_(CGraphWindow);
RegisterClass_(CGraphView);
}
//////////////////////////////////////////////////////////////////////
// SxChartApp-specific functions
//////////////////////////////////////////////////////////////////////
std::string SxChartApp::TestNumberToString(int testNumber)
{
std::ostringstream fileNumberStream;
fileNumberStream.width(2);
fileNumberStream.fill('0');
fileNumberStream << testNumber;
return fileNumberStream.str();
}
void SxChartApp::SayFileError(const std::string &rFileName)
{
std::string errorString = "Sorry, could not open the file " +
rFileName + ".";
Str255 errorPString;
::CopyCStringToPascal(errorString.c_str(), errorPString);
::ParamText(errorPString, "\p", "\p", "\p");
UModalAlerts::StopAlert(kBlankALRT);
}
void SxChartApp::RunAllTests()
{
// fetch number of tests, run each test, and output log entry
std::ifstream numTestsStream("SexChart.in");
if (!numTestsStream.is_open())
{
SxChartApp::SayFileError("SexChart.in");
return; // give up
}
int numTests = 0;
numTestsStream >> numTests;
numTestsStream.close();
RunAndLog(0, numTests - 1);
}
void SxChartApp::RunAndLog(int startCase, int endCase)
{
std::ofstream logStream("logfile.txt");
if (!logStream.is_open())
{
SxChartApp::SayFileError("logfile.txt");
return; // give up
}
for (int i = startCase; i <= endCase; i++)
{
std::clock_t startTime, endTime, elapsedTimeClocks;
startTime = std::clock();
RunOneTest(i);
endTime = std::clock();
elapsedTimeClocks = endTime - startTime;
long elapsedTimeMS =
std::floor((1000 * elapsedTimeClocks) / CLOCKS_PER_SEC + 0.5);
logStream << elapsedTimeMS << std::endl;
}
logStream.close();
}
void SxChartApp::RunOneTest(int testNumber)
{
bool bOkSoFar = true;
CTestRunner aRunner(testNumber);
bOkSoFar = aRunner.LoadNames();
if (bOkSoFar)
bOkSoFar = aRunner.LoadHookups();
if (bOkSoFar)
{
aRunner.MakeEmbeddedGraph();
aRunner.WriteLocations();
aRunner.WriteSegments();
}
}
bool SxChartApp::AskForTestNumber(int *pTestNumber)
{
SInt32 ioNumber = 0;
bool bOk = UModalDialogs::AskForOneNumber(
this,
kAskTestNumberDLOG,
kAskTestNumberEditPane,
ioNumber);
if (bOk)
*pTestNumber = ioNumber;
return bOk;
}
void SxChartApp::DrawGraph(int testNumber)
{
LWindow *pGraphWindow = LWindow::CreateWindow(kGraphWindow, this);
std::string titleString = "Graph" + TestNumberToString(testNumber);
Str255 titlePString;
::CopyCStringToPascal(titleString.c_str(), titlePString);
pGraphWindow->SetDescriptor(titlePString);
CGraphView *pGraphView =
static_cast<CGraphView *>(pGraphWindow->FindPaneByID(kGraphView));
pGraphView->LoadGraph(testNumber);
}
//////////////////////////////////////////////////////////////////////
// CTestRunner implementation
//////////////////////////////////////////////////////////////////////
CTestRunner::CTestRunner(int testNumber) :
fNumVertices(0),
fNumHookups(0),
fpGraph(0)
{
fFileNumberString = SxChartApp::TestNumberToString(testNumber);
}
CTestRunner::~CTestRunner()
{
delete fpGraph;
fpGraph = 0;
}
bool CTestRunner::LoadNames()
{
// open correct name file
std::ostringstream fileNameStream;
fileNameStream << "names" << fFileNumberString << ".in";
std::string fileName(fileNameStream.str());
std::ifstream nameStream(fileName.c_str());
if (!nameStream.is_open())
{
SxChartApp::SayFileError(fileName);
return false; // give up
}
// read number of names
std::string numNamesString;
std::getline(nameStream, numNamesString);
int numVertices = std::atoi(numNamesString.c_str());
// read all name lines
for (fNumVertices = 0;
fNumVertices < numVertices && !nameStream.eof();
fNumVertices++)
{
std::string nameLine;
std::getline(nameStream, nameLine);
fNames.push_back(nameLine);
}
nameStream.close();
// create graph this size
fpGraph = new CMyGraph(fNumVertices);
// sort names
std::sort(fNames.begin(), fNames.end());
return true;
}
bool CTestRunner::LoadHookups()
{
// open correct hookups file
std::ostringstream fileNameStream;
fileNameStream << "hookups" << fFileNumberString << ".in";
std::string fileName(fileNameStream.str());
std::ifstream hookupsStream(fileName.c_str());
if (!hookupsStream.is_open())
{
SxChartApp::SayFileError(fileName);
return false; // give up
}
// read number of hookups
std::string numHookupsString;
std::getline(hookupsStream, numHookupsString);
int numHookups = std::atoi(numHookupsString.c_str());
// read all hookup lines
for (fNumHookups = 0;
fNumHookups < numHookups && !hookupsStream.eof();
fNumHookups++)
{
std::string hookupLine;
std::getline(hookupsStream, hookupLine);
LoadOneHookup(hookupLine);
}
hookupsStream.close();
return true;
}
void CTestRunner::LoadOneHookup(const std::string& rHookupLine)
{
// format of line is
// personA,personB
std::string::size_type firstComma = rHookupLine.find(',');
std::string personAName = rHookupLine.substr(0, firstComma);
std::string personBName = rHookupLine.substr(firstComma + 1);
std::vector<std::string>::iterator iter =
std::lower_bound(fNames.begin(), fNames.end(), personAName);
int personAIndex = iter - fNames.begin();
iter = std::lower_bound(fNames.begin(), fNames.end(), personBName);
int personBIndex = iter - fNames.begin();
fpGraph->SetAdjacent(personAIndex, personBIndex);
}
void CTestRunner::MakeEmbeddedGraph()
{
// find out the connected components
fpGraph->FindConnectedComponents();
// Lay out all the points.
// We place the points at the vertices of a regular n-gon
// of fixed diameter; this is somewhat arbitrary; if we had
// some idea of the final graph we could places the points
// closer to their final position.
const int kDiam = 1000;
// diameter of circle circumscribing n-gon. units: pixels
const float kPi = 3.14159;
for (int i = 0; i < fNumVertices; i++)
{
Point vertex;
double angle = i * 2 * kPi / fNumVertices;
vertex.h = static_cast<int>(0.5 +
kDiam * 0.5 * (1 + std::cos(angle)));
vertex.v = static_cast<int>(0.5 +
kDiam * 0.5 * (1 + std::sin(-angle)));
fpGraph->SetVertex(i, vertex);
}
// embed each component in a plane, then shift it so it doesn't
// collide with the other components. We align each component flush
// left and just below the previous component.
int newCompTop = 0; // where to put next component vertically
int numComps = fpGraph->GetNumComponents();
for (int whichComp = 0; whichComp < numComps; whichComp++)
{
CGraphEmbedder aEmbedder(*fpGraph, whichComp);
aEmbedder.EmbedComponent();
Rect compBounds;
FindComponentBounds(whichComp, &compBounds);
MoveComponent(whichComp, -compBounds.left,
newCompTop - compBounds.top);
// figure out where to put next component;
// allow a little margin between components
const int kMargin = 20; // units: pixels
newCompTop += (compBounds.bottom - compBounds.top) + kMargin;
}
}
void CTestRunner::WriteLocations()
{
// open correct output file
std::ostringstream fileNameStream;
fileNameStream << "locations" << fFileNumberString << ".out";
std::string fileName(fileNameStream.str());
std::ofstream locationsStream(fileName.c_str());
if (!locationsStream.is_open())
{
SxChartApp::SayFileError(fileName);
return; // give up
}
for (int i = 0; i < fNumVertices; i++)
{
Point vertex = fpGraph->GetVertex(i);
locationsStream << vertex.h << ',';
locationsStream << vertex.v << ',';
locationsStream << fNames[i] << std::endl;
}
locationsStream.close();
}
void CTestRunner::WriteSegments()
{
// open correct output file
std::ostringstream fileNameStream;
fileNameStream << "segments" << fFileNumberString << ".out";
std::string fileName(fileNameStream.str());
std::ofstream segmentsStream(fileName.c_str());
if (!segmentsStream.is_open())
{
SxChartApp::SayFileError(fileName);
return; // give up
}
// we only need to generate lines in one direction, so we'll
// always go from lower to higher indexes
for (int startVertex = 0;
startVertex < fNumVertices;
startVertex++)
{
for (int endVertex = startVertex + 1;
endVertex < fNumVertices;
endVertex++)
{
if (fpGraph->AreAdjacent(startVertex, endVertex))
{
// we generate only straight-line segments, so
// the number of points is always 2
segmentsStream << "2" << std::endl;
Point point1 = fpGraph->GetVertex(startVertex);
segmentsStream << point1.h << ',' <<
point1.v << std::endl;
Point point2 = fpGraph->GetVertex(endVertex);
segmentsStream << point2.h << ',' <<
point2.v << std::endl;
}
}
}
segmentsStream.close();
}
void CTestRunner::FindComponentBounds(int whichComp, Rect *pCompBounds)
{
Rect bounds = {0, 0, 0, 0};
bool bBoundsEmpty = true;
for (int whichVertex = 0; whichVertex < fNumVertices; whichVertex++)
{
if (fpGraph->GetComponentNumber(whichVertex) == whichComp)
{
Point aVertex = fpGraph->GetVertex(whichVertex);
if (bBoundsEmpty)
{
bBoundsEmpty = false;
bounds.top = aVertex.v;
bounds.left = aVertex.h;
bounds.bottom = bounds.top + 1;
bounds.right = bounds.left + 1;
}
else if (!::PtInRect(aVertex, &bounds))
{
if (aVertex.v < bounds.top)
bounds.top = aVertex.v;
else if (aVertex.v >= bounds.bottom)
bounds.bottom = aVertex.v + 1;
if (aVertex.h < bounds.left)
bounds.left = aVertex.h;
else if (aVertex.h == bounds.right)
bounds.right = aVertex.h + 1;
}
}
}
*pCompBounds = bounds;
}
void CTestRunner::MoveComponent(int whichComp, int offsetH, int offsetV)
{
for (int whichVertex = 0; whichVertex < fNumVertices; whichVertex++)
{
if (fpGraph->GetComponentNumber(whichVertex) == whichComp)
{
Point aVertex = fpGraph->GetVertex(whichVertex);
aVertex.h += offsetH;
aVertex.v += offsetV;
fpGraph->SetVertex(whichVertex, aVertex);
}
}
}