[dasher: 15/28] Game mode: Draw brachistochrone if target offscreen
- From: Patrick Welche <pwelche src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dasher: 15/28] Game mode: Draw brachistochrone if target offscreen
- Date: Tue, 22 Nov 2011 17:03:55 +0000 (UTC)
commit f672eb9df54718008d903017c0dd062a714d0f59
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date: Tue Aug 3 16:46:19 2010 +0100
Game mode: Draw brachistochrone if target offscreen
Adding CDasherView::DasherSpaceArc. Also (unused, old) DrawHelperArrow
Src/DasherCore/DasherView.h | 2 +
Src/DasherCore/DasherViewSquare.cpp | 32 ++++++++++--
Src/DasherCore/DasherViewSquare.h | 12 ++++-
Src/DasherCore/GameModule.cpp | 89 ++++++++++++++++++++++++++++++++--
Src/DasherCore/GameModule.h | 8 +++-
5 files changed, 129 insertions(+), 14 deletions(-)
---
diff --git a/Src/DasherCore/DasherView.h b/Src/DasherCore/DasherView.h
index dbcd8a2..66b9a1b 100644
--- a/Src/DasherCore/DasherView.h
+++ b/Src/DasherCore/DasherView.h
@@ -132,6 +132,8 @@ public:
///Draw a straight line in Dasher-space - which may be curved on the screen...
void DasherSpaceLine(myint x1, myint y1, myint x2, myint y2, int iWidth, int iColour);
+ virtual void DasherSpaceArc(myint cy, myint r, myint x1, myint y1, myint x2, myint y2, int iColour, int iLineWidth)=0;
+
///
/// Draw a polyline specified in Dasher co-ordinates
///
diff --git a/Src/DasherCore/DasherViewSquare.cpp b/Src/DasherCore/DasherViewSquare.cpp
index 89e4092..e777ca2 100644
--- a/Src/DasherCore/DasherViewSquare.cpp
+++ b/Src/DasherCore/DasherViewSquare.cpp
@@ -361,7 +361,7 @@ void CDasherViewSquare::Circle(myint Range, myint y1, myint y2, int fCol, int oC
} else {
Dasher2Screen(x2=0,y2,p.x,p.y);
}
- CircleTo(cy,r,y1,x1,y2,x2,p,pts);
+ CircleTo(cy,r,y1,x1,y2,x2,p,pts, 2.0);
if (iDasherMaxY == y2) {
Dasher2Screen(0, iDasherMaxX, p.x, p.y);
pts.push_back(p);
@@ -372,21 +372,41 @@ void CDasherViewSquare::Circle(myint Range, myint y1, myint y2, int fCol, int oC
delete[] p_array;
}
-void CDasherViewSquare::CircleTo(myint cy, myint r, myint y1, myint x1, myint y3, myint x3, CDasherScreen::point dest, vector<CDasherScreen::point> &pts) {
+void CDasherViewSquare::CircleTo(myint cy, myint r, myint y1, myint x1, myint y3, myint x3, CDasherScreen::point dest, vector<CDasherScreen::point> &pts, double dXMul) {
myint y2((y1+y3)/2);
- myint x2(sqrt(double(sq(r)-sq(cy-y2)))*2);
+ myint x2(sqrt(double(sq(r)-sq(cy-y2)))*dXMul);
CDasherScreen::point mid; //where midpoint of circle/arc should be
Dasher2Screen(x2, y2, mid.x, mid.y); //(except "midpoint" measured along y axis)
int lmx=(pts.back().x + dest.x)/2, lmy = (pts.back().y + dest.y)/2; //midpoint of straight line
- if (dest.y-pts.back().y<2 || abs(mid.x-lmx) + abs(mid.y-lmy)<2) {
+ if (abs(dest.y-pts.back().y)<2 || abs(mid.x-lmx) + abs(mid.y-lmy)<2) {
//okay, use straight line
pts.push_back(dest);
} else {
- CircleTo(cy, r, y1, x1, y2, x2, mid, pts);
- CircleTo(cy, r, y2, x2, y3, x3, dest, pts);
+ CircleTo(cy, r, y1, x1, y2, x2, mid, pts, dXMul);
+ CircleTo(cy, r, y2, x2, y3, x3, dest, pts, dXMul);
}
}
#undef sq
+
+void CDasherViewSquare::DasherSpaceArc(myint cy, myint r, myint x1, myint y1, myint x2, myint y2, int iColour, int iLineWidth) {
+ CDasherScreen::point p;
+ //start point
+ Dasher2Screen(x1, y1, p.x, p.y);
+ vector<CDasherScreen::point> pts;
+ pts.push_back(p);
+ //if circle goes behind crosshair, force division into at least two sections, with point of max-x as boundary
+ if (r>CDasherModel::ORIGIN_X) {
+ Dasher2Screen(r, cy, p.x, p.y);
+ CircleTo(cy, r, y1, x1, cy, r, p, pts, 1.0);
+ x1=r; y1=cy;
+ }
+ Dasher2Screen(x2, y2, p.x, p.y);
+ CircleTo(cy, r, y1, x1, y2, x2, p, pts, 1.0);
+ CDasherScreen::point *p_array = new CDasherScreen::point[pts.size()];
+ for (unsigned int i=0; i<pts.size(); i++) p_array[i] = pts[i];
+ Screen()->Polyline(p_array, pts.size(), iLineWidth, iColour);
+}
+
void CDasherViewSquare::Quadric(myint Range, myint lowY, myint highY, int fillColor, int outlineColour, int lineWidth) {
static const double RR2=1.0/sqrt(2.0);
const int midY=(lowY+highY)/2;
diff --git a/Src/DasherCore/DasherViewSquare.h b/Src/DasherCore/DasherViewSquare.h
index 1b1ed39..2384083 100644
--- a/Src/DasherCore/DasherViewSquare.h
+++ b/Src/DasherCore/DasherViewSquare.h
@@ -94,13 +94,23 @@ public:
/// @}
+ void DasherSpaceArc(myint cy, myint r, myint x1, myint y1, myint x2, myint y2, int colour, int iLineWidth);
+
private:
///draw a possibly-truncated triangle given dasher-space coords & accounting for non-linearity
/// @param x = max dasher-x extent
/// @param y1, y2 = dasher-y extent along y-axis
/// @param midy1,midy2 = extent along line of max x (midy1==midy2 => triangle, midy1<midy2 => truncated tri)
void TruncateTri(myint x, myint y1, myint y2, myint midy1, myint midy2, int fillColor, int outlineColor, int lineWidth);
- void CircleTo(myint cy, myint r, myint y1, myint x1, myint y3, myint x3, CDasherScreen::point dest, vector<CDasherScreen::point> &pts);
+
+ /// compute screen coords for a circle, centered on y-axis, between two points
+ /// cy, r - dasher coords of center (on y-axis), radius
+ /// x1,y1 - one end-point of arc (dasher coords)
+ /// x2,y2 - other end-point (dasher-coords)
+ /// dest - point (x2,y2) in screen coords
+ /// pts - vector into which to store points; on entry, last element should already be screen-coords of (x1,y1)
+ /// dXMul - multiply x coords (in dasher space) by this (i.e. aspect ratio), for ovals
+ void CircleTo(myint cy, myint r, myint y1, myint x1, myint y3, myint x3, CDasherScreen::point dest, vector<CDasherScreen::point> &pts, double dXMul);
void Circle(myint Range, myint lowY, myint highY, int fCol, int oCol, int lWidth);
void Quadric(myint Range, myint lowY, myint highY, int fillColor, int outlineColour, int lineWidth);
///draw isoceles triangle, with baseline from y1-y2 along y axis (x=0), and other point at (x,(y1+y2)/2)
diff --git a/Src/DasherCore/GameModule.cpp b/Src/DasherCore/GameModule.cpp
index 7c8071a..b502f71 100644
--- a/Src/DasherCore/GameModule.cpp
+++ b/Src/DasherCore/GameModule.cpp
@@ -105,31 +105,36 @@ void CGameModule::DecorateView(unsigned long lTime, CDasherView *pView, CDasherM
if (m_dSentenceStartNats == numeric_limits<double>::max())
m_dSentenceStartNats = pModel->GetNats();
- m_vTargetY.push_back((m_y1+m_y2)/2);
+ m_vTargetY.push_back(m_iTargetY = (m_y1+m_y2)/2);
//draw a line along the y axis
myint x[2], y[2];
x[0] = x[1] = -100;
+ bool bDrawHelper=true;
+ const int lineWidth(GetLongParameter(LP_LINE_WIDTH));
+
if (m_y1 > CDasherModel::MAX_Y) {
//off the top! draw an arrow pointing up...
y[1] = CDasherModel::MAX_Y;
y[0] = y[1] - 400;
- pView->DasherPolyarrow(x, y, 2, 2, m_iCrosshairColor, 0.1);
} else if (m_y2 < 0) {
//off the bottom! draw arrow pointing down...
y[1] = 0;
y[0] = 400;
- pView->DasherPolyarrow(x, y, 2, 2, m_iCrosshairColor, 0.1);
} else {
//draw line parallel to that region of y-axis
y[0] = m_y1; y[1] = m_y2;
- pView->DasherPolyline(x, y, 2, 2, m_iCrosshairColor);
+ pView->DasherPolyline(x, y, 2, lineWidth, m_iCrosshairColor);
//and a horizontal arrow pointing to the midpoint
x[0] = -400;
- y[0] = y[1] = (m_y1+m_y2)/2;
- pView->DasherPolyarrow(x, y, 2, 2, m_iCrosshairColor, 0.1);
+ y[0] = y[1] = m_iTargetY;
+ bDrawHelper=false;
}
+ pView->DasherPolyarrow(x, y, 2, 3*lineWidth, m_iCrosshairColor, 0.2);
+
+ if (bDrawHelper) DrawBrachistochrone(pView);
+
//reset location accumulators ready for next frame
m_y1 = std::numeric_limits<myint>::min();
m_y2 = std::numeric_limits<myint>::max();
@@ -154,6 +159,78 @@ void CGameModule::DecorateView(unsigned long lTime, CDasherView *pView, CDasherM
DrawText(pView);
}
+void CGameModule::DrawBrachistochrone(CDasherView *pView) {
+ // Plot a brachistochrone - the optimal path from the crosshair to the target
+ // this is a circle, passing through both crosshair and target, centered on the y-axis
+ const myint CenterY = ComputeBrachCenter();
+ pView->DasherSpaceArc(CenterY, abs(CenterY - m_iTargetY), CDasherModel::ORIGIN_X, CDasherModel::ORIGIN_Y, 0, m_iTargetY, m_iCrosshairColor, 2*(int)GetLongParameter(LP_LINE_WIDTH));
+}
+
+void CGameModule::DrawHelperArrow(Dasher::CDasherView* pView)
+{
+ // This plots a helpful pointer to the best direction to take to get to the target.
+ // Probably too much floating point maths here, sort later.
+ // Start of line is the crosshair location
+ const int gameColour = 135; //Neon green. (!)
+ const int noOfPoints = 10; // The curve will be made up of 9 straight segments...
+ const myint m_iCrossX(CDasherModel::ORIGIN_X),m_iCrossY(CDasherModel::ORIGIN_Y);
+
+ struct {
+ myint iTargetY;
+ myint iCenterY;
+ } m_Target;
+ m_Target.iTargetY = m_iTargetY;
+ m_Target.iCenterY = ComputeBrachCenter();
+ myint iX[noOfPoints];
+ myint iY[noOfPoints];
+ myint iLength;
+
+ // Arrow starts at the cross hairs
+ iX[0] = m_iCrossX;
+ iY[0] = m_iCrossY;
+
+ myint a = m_iCrossX/5;
+ myint defaultlength = m_iCrossX - a ;
+
+ // ... then decide the length of the arrow...
+ myint r = m_Target.iTargetY-m_Target.iCenterY; // radius of our circle (+ or -)
+
+ if(m_Target.iTargetY < a && m_Target.iCenterY < m_iCrossY-defaultlength/2)
+ {
+ myint x = (myint) sqrt((double)(r*r-pow((double)(m_Target.iCenterY-a),2)));
+ iLength = (myint) sqrt((double)(pow((double)(x-m_iCrossX),2)+pow((double)(a-m_iCrossY),2)));
+ }
+ else if(m_Target.iTargetY > 2*m_iCrossY-a && m_Target.iCenterY > m_iCrossY+defaultlength/2)
+ {
+ myint x = (myint) sqrt((double)(r*r-pow((double)(m_Target.iCenterY+a-2*m_iCrossY),2)));
+ iLength = (myint) sqrt((double)(pow((double)(x-m_iCrossX),2)+pow((double)(a-m_iCrossY),2)));
+ }
+ else
+ iLength = defaultlength;
+
+ //...then calculate the points required...
+ double angle = ((double)iLength/(double)r)/(double)noOfPoints;
+
+ for(int n = 1; n < noOfPoints; ++n)
+ {
+ iX[n] = (myint) (cos(angle)*(iX[n-1]) - sin(angle)*(iY[n-1]-m_Target.iCenterY));
+ iY[n] = (myint) (m_Target.iCenterY + sin(angle)*(iX[n-1]) + cos(angle)*(iY[n-1]-m_Target.iCenterY));
+ }
+ //...then plot it.
+ pView->DasherPolyarrow(iX, iY, noOfPoints, GetLongParameter(LP_LINE_WIDTH)*4, gameColour, 1.414);
+
+}
+
+myint CGameModule::ComputeBrachCenter() {
+ const myint iCrossX(CDasherModel::ORIGIN_X), iCrossY(CDasherModel::ORIGIN_Y);
+ // This formula computes the Dasher Y Coordinate of the center of the circle on which
+ // the dasher brachistochrone lies : iCenterY
+
+ // It comes from the pythagorean relation: iCrossX^2 + (iCenterY - iCrossY)^2 = r^2
+ // where r is the radius of the circle, r = abs(iTargetY-iCenterY)
+ return 0.5*(double(iCrossX*iCrossX)/double(iCrossY-m_iTargetY)+iCrossY+m_iTargetY);
+}
+
bool CGameModule::GenerateChunk() {
m_iLastSym = -1;
m_vTargetSymbols.clear();
diff --git a/Src/DasherCore/GameModule.h b/Src/DasherCore/GameModule.h
index ecdcac0..8549cc3 100644
--- a/Src/DasherCore/GameModule.h
+++ b/Src/DasherCore/GameModule.h
@@ -67,8 +67,12 @@ protected:
virtual void ChunkGenerated() {}
/// Called when a node has been populated. Look for Game children.
- virtual void HandleEvent(CDasherNode *pNode);
+ virtual void HandleEvent(CDasherNode *pNode);
+ void DrawBrachistochrone(Dasher::CDasherView* pView);
+ void DrawHelperArrow(Dasher::CDasherView* pView);
+ myint ComputeBrachCenter();
+
/// Called when a node has been output/deleted. Update string (to be/) written.
virtual void HandleEvent(const CEditEvent *);
@@ -133,6 +137,8 @@ private:
///Time and nats at which this sentence started
unsigned long m_ulSentenceStartTime;
double m_dSentenceStartNats;
+
+ myint m_iTargetY;
/* ---------------------------------------------------------------------
* Constants
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]