Now I can add a constraint (a length), and it's displayed

on-screen, and I can drag the label. That's progress. Also
implement a bunch of untested expression stuff, since I'll need
that for the values of the dimensions, for example.

[git-p4: depot-paths = "//depot/solvespace/": change = 1668]
solver
Jonathan Westhues 2008-04-14 02:28:32 -08:00
parent 094e10204d
commit 22302dca7a
13 changed files with 433 additions and 63 deletions

View File

@ -17,6 +17,8 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
$(OBJDIR)\sketch.obj \
$(OBJDIR)\glhelper.obj \
$(OBJDIR)\expr.obj \
$(OBJDIR)\constraint.obj \
$(OBJDIR)\drawconstraint.obj \
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib

39
constraint.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "solvespace.h"
hConstraint Constraint::AddConstraint(Constraint *c) {
SS.constraint.AddAndAssignId(c);
return c->h;
}
void Constraint::MenuConstrain(int id) {
Constraint c;
memset(&c, 0, sizeof(c));
c.group = SS.GW.activeGroup;
SS.GW.GroupSelection();
#define gs (SS.GW.gs)
switch(id) {
case GraphicsWindow::MNU_DISTANCE_DIA:
if(gs.points == 2 && gs.n == 2) {
c.type = PT_PT_DISTANCE;
c.ptA = gs.point[0];
c.ptB = gs.point[1];
} else if(gs.lineSegments == 1 && gs.n == 1) {
c.type = PT_PT_DISTANCE;
c.ptA = gs.entity[0].point(16);
c.ptB = gs.entity[0].point(16+3);
} else {
Error("Bad selection for distance / diameter constraint.");
return;
}
c.disp.offset = Vector::MakeFrom(50, 50, 50);
AddConstraint(&c);
break;
default: oops();
}
SS.GW.ClearSelection();
InvalidateGraphics();
}

72
drawconstraint.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "solvespace.h"
bool Constraint::HasLabel(void) {
switch(type) {
case PT_PT_DISTANCE:
return true;
default:
return false;
}
}
void Constraint::DrawOrGetDistance(void) {
// Unit vectors that describe our current view of the scene.
Vector gr = SS.GW.projRight;
Vector gu = SS.GW.projUp;
Vector gn = gr.Cross(gu);
switch(type) {
case PT_PT_DISTANCE: {
Vector ap = SS.GetPoint(ptA)->GetCoords();
Vector bp = SS.GetPoint(ptB)->GetCoords();
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
if(dogd.drawing) {
Vector ab = ap.Minus(bp);
Vector ar = ap.Minus(ref);
// Normal to a plan containing the line and the label origin.
Vector n = ab.Cross(ar);
Vector out = ab.Cross(n).WithMagnitude(1);
out = out.ScaledBy(-out.Dot(ar));
glBegin(GL_LINES);
glxVertex3v(ap);
glxVertex3v(ap.Plus(out));
glxVertex3v(bp);
glxVertex3v(bp.Plus(out));
glEnd();
glPushMatrix();
glxTranslatev(ref);
glxOntoCsys(gr, gu);
glxWriteText("ABCDEFG");
glPopMatrix();
} else {
Point2d o = SS.GW.ProjectPoint(ref);
dogd.dmin = o.DistanceTo(dogd.mp) - 10;
}
break;
}
default: oops();
}
}
void Constraint::Draw(void) {
dogd.drawing = true;
DrawOrGetDistance();
}
double Constraint::GetDistance(Point2d mp) {
dogd.drawing = false;
dogd.mp = mp;
dogd.dmin = 1e12;
DrawOrGetDistance();
return dogd.dmin;
}

10
dsc.h
View File

@ -19,6 +19,7 @@ public:
Vector Normal(int which);
Vector RotatedAbout(Vector axis, double theta);
double Magnitude(void);
Vector WithMagnitude(double s);
Vector ScaledBy(double s);
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
@ -103,6 +104,15 @@ public:
}
}
void Tag(H h, int tag) {
int i;
for(i = 0; i < elems; i++) {
if(elem[i].t.h.v == h.v) {
elem[i].tag = tag;
}
}
}
void RemoveTagged(void) {
int src, dest;
dest = 0;

View File

@ -44,3 +44,73 @@ double Expr::Eval(void) {
}
}
Expr *Expr::PartialWrt(hParam p) {
Expr *da, *db;
switch(op) {
case PARAM_PTR: oops();
case PARAM: return FromConstant(p.v == x.parh.v ? 1 : 0);
case CONSTANT: return FromConstant(0);
case PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p));
case MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p));
case TIMES:
da = a->PartialWrt(p);
db = b->PartialWrt(p);
return (a->Times(db))->Plus(b->Times(da));
case DIV:
da = a->PartialWrt(p);
db = b->PartialWrt(p);
return ((da->Times(b))->Minus(a->Times(db)))->Div(b->Square());
case SQRT:
return (FromConstant(0.5)->Div(a->Sqrt()))->Times(a->PartialWrt(p));
case SQUARE:
return (FromConstant(2.0)->Times(a))->Times(a->PartialWrt(p));
case NEGATE: return (a->PartialWrt(p))->Negate();
case SIN: return (a->Cos())->Times(a->PartialWrt(p));
case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
default: oops();
}
}
static char StringBuffer[4096];
void Expr::App(char *s, ...) {
va_list f;
va_start(f, s);
vsprintf(StringBuffer+strlen(StringBuffer), s, f);
}
char *Expr::Print(void) {
StringBuffer[0] = '\0';
PrintW();
return StringBuffer;
}
void Expr::PrintW(void) {
switch(op) {
case PARAM: App("(param %08x)", x.parh.v); break;
case PARAM_PTR: App("(paramp %08x)", x.parp->h.v); break;
case CONSTANT: App("%.3f", x.v);
case PLUS: App("(+ "); a->PrintW(); b->PrintW(); App(")"); break;
case MINUS: App("(- "); a->PrintW(); b->PrintW(); App(")"); break;
case TIMES: App("(* "); a->PrintW(); b->PrintW(); App(")"); break;
case DIV: App("(/ "); a->PrintW(); b->PrintW(); App(")"); break;
case NEGATE: App("(- "); a->PrintW(); App(")"); break;
case SQRT: App("(sqrt "); a->PrintW(); App(")"); break;
case SQUARE: App("(square "); a->PrintW(); App(")"); break;
case SIN: App("(sin "); a->PrintW(); App(")"); break;
case COS: App("(cos "); a->PrintW(); App(")"); break;
default: oops();
}
}

5
expr.h
View File

@ -58,7 +58,10 @@ public:
double Eval(void);
void ParamsToPointers(void);
void Print(void);
void App(char *str, ...);
char *Print(void);
void PrintW(void); // worker
};
#endif

View File

@ -5,6 +5,7 @@
#define mView (&GraphicsWindow::MenuView)
#define mEdit (&GraphicsWindow::MenuEdit)
#define mReq (&GraphicsWindow::MenuRequest)
#define mCon (&Constraint::MenuConstrain)
#define S 0x100
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 0, "&File", 0, NULL },
@ -19,6 +20,8 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Undo\tCtrl+Z", 0, NULL },
{ 1, "&Redo\tCtrl+Y", 0, NULL },
{ 1, NULL, 0, NULL },
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
{ 1, NULL, 0, NULL },
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
{ 0, "&View", 0, NULL },
@ -27,7 +30,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
{ 1, NULL, 0, NULL },
{ 1, "&Onto Plane / Coordinate System\tO", MNU_ORIENT_ONTO, 'O', mView },
{ 1, "&Lock Orientation\tL", 0, 'L', mView },
{ 1, "&Lock Orientation\tL", MNU_LOCK_VIEW, 'L', mView },
{ 1, NULL, 0, NULL },
{ 1, "Dimensions in &Inches", 0, NULL },
{ 1, "Dimensions in &Millimeters", 0, NULL },
@ -54,7 +57,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "To&ggle Construction\tG", 0, 'G', NULL },
{ 0, "&Constrain", 0, NULL },
{ 1, "&Distance / Diameter\tShift+D", 0, 'D'|S, NULL },
{ 1, "&Distance / Diameter\tShift+D", MNU_DISTANCE_DIA, 'D'|S, mCon },
{ 1, "A&ngle\tShift+N", 0, 'N'|S, NULL },
{ 1, "Other S&upplementary Angle\tShift+U", 0, 'U'|S, NULL },
{ 1, NULL, 0, NULL },
@ -108,7 +111,7 @@ Point2d GraphicsWindow::ProjectPoint(Vector p) {
return r;
}
void GraphicsWindow::MenuView(MenuId id) {
void GraphicsWindow::MenuView(int id) {
switch(id) {
case MNU_ZOOM_IN:
SS.GW.scale *= 1.2;
@ -121,6 +124,11 @@ void GraphicsWindow::MenuView(MenuId id) {
case MNU_ZOOM_TO_FIT:
break;
case MNU_LOCK_VIEW:
SS.GW.viewLocked = !SS.GW.viewLocked;
CheckMenuById(MNU_LOCK_VIEW, SS.GW.viewLocked);
break;
case MNU_ORIENT_ONTO:
SS.GW.GroupSelection();
if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) {
@ -155,7 +163,7 @@ void GraphicsWindow::EnsureValidActiveGroup(void) {
activeGroup = SS.group.elem[i].t.h;
}
void GraphicsWindow::MenuEdit(MenuId id) {
void GraphicsWindow::MenuEdit(int id) {
switch(id) {
case MNU_UNSELECT_ALL:
SS.GW.ClearSelection();
@ -164,11 +172,38 @@ void GraphicsWindow::MenuEdit(MenuId id) {
SS.TW.Show();
break;
case MNU_DELETE: {
int i;
SS.request.ClearTags();
for(i = 0; i < MAX_SELECTED; i++) {
Selection *s = &(SS.GW.selection[i]);
hRequest r;
r.v = 0;
if(s->point.v) {
Point *pt = SS.GetPoint(s->point);
Entity *e = SS.GetEntity(pt->entity());
if(e->type == Entity::DATUM_POINT) {
r = e->request();
}
} else if(s->entity.v) {
Entity *e = SS.GetEntity(s->entity);
r = e->request();
}
if(r.v) SS.request.Tag(r, 1);
}
SS.request.RemoveTagged();
SS.GenerateAll();
SS.GW.ClearSelection();
SS.GW.hover.Clear();
break;
}
default: oops();
}
}
void GraphicsWindow::MenuRequest(MenuId id) {
void GraphicsWindow::MenuRequest(int id) {
char *s;
switch(id) {
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
@ -183,12 +218,16 @@ c:
}
}
void GraphicsWindow::UpdateDraggedPoint(hPoint hp, double mx, double my) {
void GraphicsWindow::UpdateDraggedHPoint(hPoint hp, double mx, double my) {
Point *p = SS.point.FindById(hp);
Vector pos = p->GetCoords();
pos = pos.Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale));
pos = pos.Plus(projUp.ScaledBy((my - orig.mouse.y)/scale));
UpdateDraggedPoint(&pos, mx, my);
p->ForceTo(pos);
}
void GraphicsWindow::UpdateDraggedPoint(Vector *pos, double mx, double my) {
*pos = pos->Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale));
*pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/scale));
orig.mouse.x = mx;
orig.mouse.y = my;
@ -206,11 +245,12 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
double dx = (x - orig.mouse.x) / scale;
double dy = (y - orig.mouse.y) / scale;
if(shiftDown) {
// When the view is locked, permit only translation (pan).
if(shiftDown || viewLocked) {
offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
} else if(ctrlDown) {
} else if(ctrlDown && !viewLocked) {
double theta = atan2(orig.mouse.y, orig.mouse.x);
theta -= atan2(y, x);
@ -219,7 +259,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
projUp = orig.projUp.RotatedAbout(normal, theta);
NormalizeProjectionVectors();
} else {
} else if(!viewLocked) {
double s = 0.3*(PI/180); // degrees per pixel
projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);
@ -235,14 +275,21 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
InvalidateGraphics();
} else if(leftDown) {
// We are left-dragging. This is often used to drag points.
// We are left-dragging. This is often used to drag points, or
// constraint labels.
if(hover.point.v && !hover.point.isFromReferences()) {
ClearSelection();
UpdateDraggedPoint(hover.point, x, y);
UpdateDraggedHPoint(hover.point, x, y);
} else if(hover.constraint.v &&
SS.GetConstraint(hover.constraint)->HasLabel())
{
ClearSelection();
Constraint *c = SS.constraint.FindById(hover.constraint);
UpdateDraggedPoint(&(c->disp.offset), x, y);
}
} else {
if(pendingOperation == PENDING_OPERATION_DRAGGING_POINT) {
UpdateDraggedPoint(pendingPoint, x, y);
UpdateDraggedHPoint(pendingPoint, x, y);
} else {
// Do our usual hit testing, for the selection.
Selection s;
@ -256,35 +303,37 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
}
bool GraphicsWindow::Selection::Equals(Selection *b) {
if(point.v != b->point.v) return false;
if(entity.v != b->entity.v) return false;
if(point.v != b->point.v) return false;
if(entity.v != b->entity.v) return false;
if(constraint.v != b->constraint.v) return false;
return true;
}
bool GraphicsWindow::Selection::IsEmpty(void) {
if(point.v) return false;
if(entity.v) return false;
if(point.v) return false;
if(entity.v) return false;
if(constraint.v) return false;
return true;
}
void GraphicsWindow::Selection::Clear(void) {
point.v = entity.v = 0;
point.v = entity.v = constraint.v = 0;
}
void GraphicsWindow::Selection::Draw(void) {
if(point.v) SS.point. FindById(point )->Draw();
if(entity.v) SS.entity.FindById(entity)->Draw();
if(point.v) SS.point. FindById(point )->Draw();
if(entity.v) SS.entity. FindById(entity )->Draw();
if(constraint.v) SS.constraint.FindById(constraint)->Draw();
}
void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
int i;
double d, dmin = 1e12;
dest->point.v = 0;
dest->entity.v = 0;
memset(dest, 0, sizeof(*dest));
// Do the points
for(i = 0; i < SS.entity.elems; i++) {
d = SS.entity.elem[i].t.GetDistance(mp);
if(d < 10 && d < dmin) {
dest->point.v = 0;
memset(dest, 0, sizeof(*dest));
dest->entity = SS.entity.elem[i].t.h;
}
}
@ -293,10 +342,19 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp, Selection *dest) {
for(i = 0; i < SS.point.elems; i++) {
d = SS.point.elem[i].t.GetDistance(mp);
if(d < 10 && d < dmin) {
dest->entity.v = 0;
memset(dest, 0, sizeof(*dest));
dest->point = SS.point.elem[i].t.h;
}
}
// Constraints
for(i = 0; i < SS.constraint.elems; i++) {
d = SS.constraint.elem[i].t.GetDistance(mp);
if(d < 10 && d < dmin) {
memset(dest, 0, sizeof(*dest));
dest->constraint = SS.constraint.elem[i].t.h;
}
}
}
void GraphicsWindow::ClearSelection(void) {
@ -308,7 +366,7 @@ void GraphicsWindow::ClearSelection(void) {
void GraphicsWindow::GroupSelection(void) {
gs.points = gs.entities = 0;
gs.csyss = 0;
gs.csyss = gs.lineSegments = 0;
gs.n = 0;
int i;
for(i = 0; i < MAX_SELECTED; i++) {
@ -322,8 +380,9 @@ void GraphicsWindow::GroupSelection(void) {
(gs.n)++;
Entity *e = SS.entity.FindById(s->entity);
if(e->type == Entity::CSYS_2D) {
(gs.csyss)++;
switch(e->type) {
case Entity::CSYS_2D: (gs.csyss)++; break;
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
}
}
}
@ -496,6 +555,10 @@ void GraphicsWindow::Paint(int w, int h) {
for(i = 0; i < SS.point.elems; i++) {
SS.point.elem[i].t.Draw();
}
glColor3f(1.0f, 0, 1.0f);
for(i = 0; i < SS.constraint.elems; i++) {
SS.constraint.elem[i].t.Draw();
}
// Then redraw whatever the mouse is hovering over, highlighted. Have
// to disable the depth test, so that we can overdraw.

View File

@ -12,6 +12,9 @@ class Entity;
class Param;
class Point;
class hEquation;
class Equation;
// All of the hWhatever handles are a 32-bit ID, that is used to represent
// some data structure in the sketch.
class hGroup {
@ -79,9 +82,9 @@ public:
static const hRequest HREQUEST_REFERENCE_ZX;
// Types of requests
static const int CSYS_2D = 0;
static const int DATUM_POINT = 1;
static const int LINE_SEGMENT = 10;
static const int CSYS_2D = 10;
static const int DATUM_POINT = 11;
static const int LINE_SEGMENT = 20;
int type;
@ -173,6 +176,69 @@ public:
double GetDistance(Point2d mp);
};
class hConstraint {
public:
DWORD v;
};
class Constraint {
public:
static const int USER_EQUATION = 10;
static const int POINTS_COINCIDENT = 20;
static const int PT_PT_DISTANCE = 30;
static const int PT_LINE_DISTANCE = 31;
static const int HORIZONTAL = 40;
static const int VERTICAL = 41;
hConstraint h;
int type;
hGroup group;
// These are the parameters for the constraint.
Expr *exprA;
Expr *exprB;
hPoint ptA;
hPoint ptB;
hPoint ptC;
hEntity entityA;
hEntity entityB;
// These define how the constraint is drawn on-screen.
struct {
hEntity csys;
Vector offset;
Vector u, v;
} disp;
static hConstraint AddConstraint(Constraint *c);
static void MenuConstrain(int id);
struct {
bool drawing;
Point2d mp;
double dmin;
} dogd; // state for drawing or getting distance (for hit testing)
double GetDistance(Point2d mp);
void Draw(void);
void DrawOrGetDistance(void);
bool HasLabel(void);
void Generate(IdList<Equation,hEquation> *l);
};
class hEquation {
public:
DWORD v;
};
class Equation {
public:
hEquation h;
Expr *e;
};
inline hEntity hRequest::entity(int i)
{ hEntity r; r.v = (v << 10) | i; return r; }

View File

@ -1,17 +1,15 @@
#include "solvespace.h"
template IdList<Request,hRequest>;
template IdList<Entity,hEntity>;
template IdList<Point,hPoint>;
SolveSpace SS;
void SolveSpace::Init(void) {
constraint.Clear();
request.Clear();
group.Clear();
entity.Clear();
point.Clear();
param.Clear();
group.Clear();
// Our initial group, that contains the references.
Group g;

View File

@ -12,13 +12,6 @@
#define max(x, y) ((x) > (y) ? (x) : (y))
#endif
class Expr;
void dbp(char *str, ...);
void Error(char *str, ...);
Expr *AllocExpr(void);
void FreeAllExprs(void);
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -27,14 +20,23 @@ void FreeAllExprs(void);
#include <gl/gl.h>
#include <gl/glu.h>
class Expr;
// From the platform-specific code.
void CheckMenuById(int id, BOOL checked);
void InvalidateGraphics(void);
void InvalidateText(void);
void dbp(char *str, ...);
void Error(char *str, ...);
Expr *AllocExpr(void);
void FreeAllExprs(void);
#include "dsc.h"
#include "sketch.h"
#include "ui.h"
#include "expr.h"
// From the platform-specific code.
void InvalidateGraphics(void);
void InvalidateText(void);
// Utility functions that are provided in the platform-independent code.
void glxVertex3v(Vector u);
@ -56,14 +58,22 @@ public:
TextWindow TW;
GraphicsWindow GW;
IdList<Group,hGroup> group;
IdList<Request,hRequest> request;
IdList<Entity,hEntity> entity;
IdList<Point,hPoint> point;
IdList<Param,hParam> param;
// These lists define the sketch, and are edited by the user.
IdList<Group,hGroup> group;
IdList<Request,hRequest> request;
IdList<Constraint,hConstraint> constraint;
inline Entity *GetEntity(hEntity h) { return entity.FindById(h); }
inline Param *GetParam (hParam h) { return param. FindById(h); }
// These lists are generated automatically when we solve the sketch.
IdList<Entity,hEntity> entity;
IdList<Point,hPoint> point;
IdList<Param,hParam> param;
inline Constraint *GetConstraint(hConstraint h)
{ return constraint.FindById(h); }
inline Request *GetRequest(hRequest h) { return request.FindById(h); }
inline Entity *GetEntity (hEntity h) { return entity. FindById(h); }
inline Param *GetParam (hParam h) { return param. FindById(h); }
inline Point *GetPoint (hPoint h) { return point. FindById(h); }
hGroup activeGroup;

19
ui.h
View File

@ -78,12 +78,17 @@ public:
MNU_ZOOM_OUT,
MNU_ZOOM_TO_FIT,
MNU_ORIENT_ONTO,
MNU_LOCK_VIEW,
MNU_UNSELECT_ALL,
// Edit
MNU_DELETE,
// Request
MNU_DATUM_POINT,
MNU_LINE_SEGMENT,
// Constrain
MNU_DISTANCE_DIA,
} MenuId;
typedef void MenuHandler(MenuId id);
typedef void MenuHandler(int id);
typedef struct {
int level; // 0 == on menu bar, 1 == one level down
char *label; // or NULL for a separator
@ -92,9 +97,9 @@ public:
MenuHandler *fn;
} MenuEntry;
static const MenuEntry menu[];
static void MenuView(MenuId id);
static void MenuEdit(MenuId id);
static void MenuRequest(MenuId id);
static void MenuView(int id);
static void MenuEdit(int id);
static void MenuRequest(int id);
// The width and height (in pixels) of the window.
double width, height;
@ -111,6 +116,7 @@ public:
Vector projUp;
Point2d mouse;
} orig;
bool viewLocked;
void NormalizeProjectionVectors(void);
Point2d ProjectPoint(Vector p);
@ -132,6 +138,7 @@ public:
public:
hPoint point;
hEntity entity;
hConstraint constraint;
void Draw(void);
@ -150,6 +157,7 @@ public:
int points;
int entities;
int csyss;
int lineSegments;
int n;
} gs;
void GroupSelection(void);
@ -163,7 +171,8 @@ public:
static void ToggleBool(int link, DWORD v);
static void ToggleAnyDatumShown(int link, DWORD v);
void UpdateDraggedPoint(hPoint hp, double mx, double my);
void UpdateDraggedPoint(Vector *pos, double mx, double my);
void UpdateDraggedHPoint(hPoint hp, double mx, double my);
// These are called by the platform-specific code.
void Paint(int w, int h);

View File

@ -120,7 +120,7 @@ Vector Vector::Normal(int which) {
oops();
}
n = n.ScaledBy(1/n.Magnitude());
n = n.WithMagnitude(1);
return n;
}
@ -160,6 +160,15 @@ Vector Vector::ScaledBy(double v) {
return r;
}
Vector Vector::WithMagnitude(double v) {
double m = Magnitude();
if(m < 0.001) {
return MakeFrom(v, 0, 0);
} else {
return ScaledBy(v/Magnitude());
}
}
Point2d Point2d::Plus(Point2d b) {
Point2d r;
r.x = x + b.x;

View File

@ -39,7 +39,6 @@ void dbp(char *str, ...)
va_start(f, str);
vsprintf(buf, str, f);
OutputDebugString(buf);
OutputDebugString("\n");
}
void Error(char *str, ...)
@ -300,8 +299,8 @@ static BOOL ProcessKeyDown(WPARAM wParam)
c = wParam;
break;
}
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x100;
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x200;
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x100;
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x200;
for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
if(c == SS.GW.menu[i].accel) {
@ -443,6 +442,26 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
return 1;
}
void CheckMenuById(int id, BOOL checked)
{
int i;
int subMenu = -1;
for(i = 0; SS.GW.menu[i].level >= 0; i++) {
if(SS.GW.menu[i].level == 0) subMenu++;
if(SS.GW.menu[i].id == id) {
if(subMenu < 0) oops();
if(subMenu >= (sizeof(SubMenus)/sizeof(SubMenus[0]))) oops();
CheckMenuItem(SubMenus[subMenu], id,
checked ? MF_CHECKED : MF_UNCHECKED);
return;
}
}
oops();
}
HMENU CreateGraphicsWindowMenus(void)
{
HMENU top = CreateMenu();