Add helical sweeps. These aren't as parametric as I would have

liked, but my more parametric attempts were very difficult to use.
The pitch (both axial and radial) gets specified by typing a
distance in a textbox.

[git-p4: depot-paths = "//depot/solvespace/": change = 1804]
This commit is contained in:
Jonathan Westhues 2008-06-23 00:25:17 -08:00
parent fe75efc6aa
commit 3ddd1703b1
9 changed files with 209 additions and 35 deletions

View File

@ -363,7 +363,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
case MNU_RECTANGLE: {
if(!SS.GW.LockedInWorkplane()) {
Error("Can't draw rectangle in 3d; select a workplane first.");
break;
ClearSuper();
break;
}
hRequest lns[4];
int i;

View File

@ -67,6 +67,8 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) },
{ 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) },
{ 'g', "Group.valA", 'f', &(SS.sv.g.valA) },
{ 'g', "Group.valB", 'f', &(SS.sv.g.valB) },
{ 'g', "Group.valC", 'f', &(SS.sv.g.valB) },
{ 'g', "Group.color", 'x', &(SS.sv.g.color) },
{ 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) },
{ 'g', "Group.skipFirst", 'b', &(SS.sv.g.skipFirst) },

View File

@ -48,6 +48,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "E&xtrude\tShift+Ctrl+X", MNU_GROUP_EXTRUDE, 'X'|S|C,mGrp },
{ 1, "&Lathe\tShift+Ctrl+L", MNU_GROUP_LATHE, 'L'|S|C,mGrp },
{ 1, "&Sweep\tShift+Ctrl+S", MNU_GROUP_SWEEP, 'S'|S|C,mGrp },
{ 1, "&Helical Sweep\tShift+Ctrl+H", MNU_GROUP_HELICAL, 'H'|S|C,mGrp },
{ 1, NULL, 0, 0, NULL },
{ 1, "Import / Assemble...\tShift+Ctrl+I", MNU_GROUP_IMPORT, 'I'|S|C,mGrp },
{11, "Import Recent", MNU_GROUP_RECENT, 0, mGrp },

View File

@ -20,6 +20,7 @@ void Group::MenuGroup(int id) {
Group g;
ZERO(&g);
g.visible = true;
g.color = RGB(100, 100, 100);
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
@ -75,7 +76,6 @@ void Group::MenuGroup(int id) {
case GraphicsWindow::MNU_GROUP_EXTRUDE:
g.type = EXTRUDE;
g.opA = SS.GW.activeGroup;
g.color = RGB(100, 100, 100);
g.predef.entityB = SS.GW.ActiveWorkplane();
g.subtype = ONE_SIDED;
g.name.strcpy("extrude");
@ -95,7 +95,6 @@ void Group::MenuGroup(int id) {
}
g.type = LATHE;
g.opA = SS.GW.activeGroup;
g.color = RGB(100, 100, 100);
g.name.strcpy("lathe");
SS.GW.ClearSelection();
break;
@ -119,11 +118,38 @@ void Group::MenuGroup(int id) {
}
// The active group is our section
g.opB = SS.GW.activeGroup;
g.color = RGB(100, 100, 100);
g.name.strcpy("sweep");
break;
}
case GraphicsWindow::MNU_GROUP_HELICAL: {
if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
Vector pt = SS.GetEntity(gs.point[0])->PointGetNum();
Entity *ln = SS.GetEntity(gs.entity[0]);
Vector lpa = SS.GetEntity(ln->point[0])->PointGetNum();
Vector lpb = SS.GetEntity(ln->point[1])->PointGetNum();
double d = pt.DistanceToLine(lpa, lpb.Minus(lpa));
if(d < LENGTH_EPS) {
Error("Point on helix can't lie on helix's axis!");
return;
}
g.predef.origin = gs.point[0];
g.predef.entityB = gs.entity[0];
} else {
Error("Bad selection for helical sweep.");
return;
}
g.type = HELICAL_SWEEP;
g.subtype = RIGHT_HANDED;
g.valA = 3; // turns;
g.valB = 300/SS.GW.scale; // pitch along axis
g.valC = 0; // pitch in radius
g.opA = SS.GW.activeGroup;
g.name.strcpy("helical-sweep");
SS.GW.ClearSelection();
break;
}
case GraphicsWindow::MNU_GROUP_ROT: {
if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
g.predef.origin = gs.point[0];
@ -315,6 +341,10 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
break;
}
case HELICAL_SWEEP: {
break;
}
case TRANSLATE: {
// The translation vector
AddParam(param, h.param(0), gp.x);

View File

@ -38,9 +38,10 @@ void Group::GeneratePolygon(void) {
void Group::GetTrajectory(hGroup hg, SContour *traj, SPolygon *section) {
if(section->IsEmpty()) return;
SEdgeList edges; ZERO(&edges);
int i, j;
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(e->group.v != hg.v) continue;
@ -100,7 +101,6 @@ void Group::GetTrajectory(hGroup hg, SContour *traj, SPolygon *section) {
// The starting point is the endpoint that's closer to the plane
traj->Reverse();
}
cleanup:
edges.Clear();
}
@ -197,20 +197,41 @@ void Group::GenerateMeshForStepAndRepeat(void) {
thisMesh.Clear();
}
void Group::GenerateMeshForSweep(void) {
void Group::GenerateMeshForSweep(bool helical,
Vector axisp, Vector axis, Vector onHelix)
{
STriMeta meta = { 0, color };
SEdgeList edges;
ZERO(&edges);
int a, i;
// The closed section that will be swept along the curve
Group *section = SS.GetGroup(opB);
Group *section = SS.GetGroup(helical ? opA : opB);
SEdgeList edges;
ZERO(&edges);
(section->poly).MakeEdgesInto(&edges);
// The trajectory along which the section will be swept
SContour traj;
ZERO(&traj);
GetTrajectory(opA, &traj, &(section->poly));
if(helical) {
double r0 = onHelix.DistanceToLine(axisp, axis);
int n = (int)(SS.CircleSides(r0)*valA) + 4;
Vector origin = onHelix.ClosestPointOnLine(axisp, axis);
Vector u = (onHelix.Minus(origin)).WithMagnitude(1);
Vector v = (axis.Cross(u)).WithMagnitude(1);
for(i = 0; i <= n; i++) {
double turns = (i*valA)/n;
double theta = turns*2*PI;
double r = r0 + turns*valC;
if(subtype == LEFT_HANDED) theta = -theta;
Vector p = origin.Plus(
u.ScaledBy(r*cos(theta)).Plus(
v.ScaledBy(r*sin(theta)).Plus(
axis.WithMagnitude(turns*valB))));
traj.AddPoint(p);
}
} else {
GetTrajectory(opA, &traj, &(section->poly));
}
if(traj.l.n <= 0) {
edges.Clear();
@ -222,7 +243,17 @@ void Group::GenerateMeshForSweep(void) {
Vector origNormal = (traj.l.elem[1].p).Minus(origRef);
origNormal = origNormal.WithMagnitude(1);
Vector oldRef = origRef, oldNormal = origNormal;
Vector oldU = oldNormal.Normal(0), oldV = oldNormal.Normal(1);
Vector oldU, oldV;
if(helical) {
oldU = axis.WithMagnitude(1);
oldV = (oldNormal.Cross(oldU)).WithMagnitude(1);
// numerical fixup, since pwl segment isn't exactly tangent...
oldU = (oldV.Cross(oldNormal)).WithMagnitude(1);
} else {
oldU = oldNormal.Normal(0);
oldV = oldNormal.Normal(1);
}
// The endcap at the start of the curve
SPolygon cap;
@ -257,11 +288,18 @@ void Group::GenerateMeshForSweep(void) {
useNormal = (thisNormal.Plus(oldNormal)).ScaledBy(0.5);
}
// Choose a new coordinate system, normal to the trajectory and
// with the minimum possible twist about the normal.
Vector useV, useU;
useNormal = useNormal.WithMagnitude(1);
Vector useV = (useNormal.Cross(oldU)).WithMagnitude(1);
Vector useU = (useV.Cross(useNormal)).WithMagnitude(1);
if(helical) {
// The axis of rotation is always a basis vector
useU = axis.WithMagnitude(1);
useV = (useNormal.Cross(useU)).WithMagnitude(1);
} else {
// Choose a new coordinate system, normal to the trajectory and
// with the minimum possible twist about the normal.
useV = (useNormal.Cross(oldU)).WithMagnitude(1);
useU = (useV.Cross(useNormal)).WithMagnitude(1);
}
Quaternion qi = Quaternion::From(oldU, oldV);
Quaternion qf = Quaternion::From(useU, useV);
@ -432,7 +470,14 @@ void Group::GenerateMesh(void) {
}
}
} else if(type == SWEEP) {
GenerateMeshForSweep();
Vector zp = Vector::From(0, 0, 0);
GenerateMeshForSweep(false, zp, zp, zp);
} else if(type == HELICAL_SWEEP) {
Entity *ln = SS.GetEntity(predef.entityB);
Vector lna = SS.GetEntity(ln->point[0])->PointGetNum(),
lnb = SS.GetEntity(ln->point[1])->PointGetNum();
Vector onh = SS.GetEntity(predef.origin)->PointGetNum();
GenerateMeshForSweep(true, lna, lnb.Minus(lna), onh);
} else if(type == IMPORTED) {
// Triangles are just copied over, with the appropriate transformation
// applied.

View File

@ -83,6 +83,7 @@ public:
static const int EXTRUDE = 5100;
static const int LATHE = 5101;
static const int SWEEP = 5102;
static const int HELICAL_SWEEP = 5103;
static const int ROTATE = 5200;
static const int TRANSLATE = 5201;
static const int IMPORTED = 5300;
@ -94,6 +95,8 @@ public:
bool clean;
hEntity activeWorkplane;
double valA;
double valB;
double valC;
DWORD color;
static const int SOLVED_OKAY = 0;
@ -104,10 +107,15 @@ public:
SList<hConstraint> remove;
} solved;
// For drawings in 2d
static const int WORKPLANE_BY_POINT_ORTHO = 6000;
static const int WORKPLANE_BY_LINE_SEGMENTS = 6001;
// For extrudes, translates, and rotates
static const int ONE_SIDED = 7000;
static const int TWO_SIDED = 7001;
// For helical sweeps
static const int RIGHT_HANDED = 8000;
static const int LEFT_HANDED = 8001;
int subtype;
bool skipFirst; // for step and repeat ops
@ -158,8 +166,8 @@ public:
void Activate(void);
char *DescriptionString(void);
static void AddParam(IdList<Param,hParam> *param, hParam hp, double v);
void Generate(IdList<Entity,hEntity> *entity, IdList<Param,hParam> *param);
static void AddParam(ParamList *param, hParam hp, double v);
void Generate(EntityList *entity, ParamList *param);
// When a request generates entities from entities, and the source
// entities may have come from multiple requests, it's necessary to
// remap the entity ID so that it's still unique. We do this with a
@ -170,10 +178,10 @@ public:
static const int REMAP_PT_TO_LINE = 1003;
static const int REMAP_LINE_TO_FACE = 1004;
hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in);
void MakeExtrusionTopBottomFaces(IdList<Entity,hEntity> *el, hEntity pt);
void MakeExtrusionLines(EntityList *el, hEntity in);
void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt);
void TagEdgesFromLineSegments(SEdgeList *sle);
void CopyEntity(IdList<Entity,hEntity> *el,
void CopyEntity(EntityList *el,
Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
@ -190,7 +198,8 @@ public:
void AddQuadWithNormal(STriMeta meta, Vector out,
Vector a, Vector b, Vector c, Vector d);
void GenerateMeshForStepAndRepeat(void);
void GenerateMeshForSweep(void);
void GenerateMeshForSweep(bool helical,
Vector axisp, Vector axis, Vector onHelix);
void GenerateMesh(void);
void Draw(void);
@ -226,8 +235,8 @@ public:
bool construction;
static hParam AddParam(IdList<Param,hParam> *param, hParam hp);
void Generate(IdList<Entity,hEntity> *entity, IdList<Param,hParam> *param);
static hParam AddParam(ParamList *param, hParam hp);
void Generate(EntityList *entity, ParamList *param);
char *DescriptionString(void);
};

View File

@ -101,7 +101,10 @@ void vl(void); // debug function to validate
class Entity;
class hEntity;
class Param;
class hParam;
typedef IdList<Entity,hEntity> EntityList;
typedef IdList<Param,hParam> ParamList;
#include "sketch.h"
#include "ui.h"
@ -144,8 +147,8 @@ class System {
public:
#define MAX_UNKNOWNS 200
IdList<Entity,hEntity> entity;
IdList<Param,hParam> param;
EntityList entity;
ParamList param;
IdList<Equation,hEquation> eq;
// In general, the tag indicates the subsys that a variable/equation

View File

@ -626,6 +626,39 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
SS.GenerateAll();
SS.GW.ClearSuper();
}
void TextWindow::ScreenChangeRightLeftHanded(int link, DWORD v) {
SS.UndoRemember();
Group *g = SS.GetGroup(SS.TW.shown->group);
if(g->subtype == Group::RIGHT_HANDED) {
g->subtype = Group::LEFT_HANDED;
} else {
g->subtype = Group::RIGHT_HANDED;
}
SS.MarkGroupDirty(g->h);
SS.GenerateAll();
SS.GW.ClearSuper();
}
void TextWindow::ScreenChangeHelixParameter(int link, DWORD v) {
Group *g = SS.GetGroup(SS.TW.shown->group);
char str[1024];
int r;
if(link == 't') {
sprintf(str, "%.3f", g->valA);
SS.TW.edit.meaning = EDIT_HELIX_TURNS;
r = 12;
} else if(link == 'p') {
strcpy(str, SS.MmToString(g->valB));
SS.TW.edit.meaning = EDIT_HELIX_PITCH;
r = 14;
} else if(link == 'r') {
strcpy(str, SS.MmToString(g->valC));
SS.TW.edit.meaning = EDIT_HELIX_DRADIUS;
r = 16;
} else oops();
SS.TW.edit.group.v = v;
ShowTextEditControl(r, 9, str);
}
void TextWindow::ScreenColor(int link, DWORD v) {
SS.UndoRemember();
@ -683,10 +716,6 @@ void TextWindow::ShowGroupInfo(void) {
g->h.v, &TextWindow::ScreenDeleteGroup);
}
if(g->type == Group::IMPORTED) {
Printf(true, "%FtIMPORT%E '%s'", g->impFile);
}
if(g->type == Group::EXTRUDE) {
s = "EXTRUDE ";
} else if(g->type == Group::TRANSLATE) {
@ -708,11 +737,32 @@ void TextWindow::ShowGroupInfo(void) {
(one ? "" : "one side"), (one ? "one side" : ""),
&TextWindow::ScreenChangeOneOrTwoSides,
(!one ? "" : "two sides"), (!one ? "two sides" : ""));
}
}
if(g->type == Group::LATHE) {
Printf(true, "%FtLATHE");
}
if(g->type == Group::SWEEP) {
Printf(true, "%FtSWEEP");
}
if(g->type == Group::HELICAL_SWEEP) {
bool rh = (g->subtype == Group::RIGHT_HANDED);
Printf(true,
"%FtHELICAL%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E",
&ScreenChangeRightLeftHanded,
(rh ? "" : "right-hand"), (rh ? "right-hand" : ""),
&ScreenChangeRightLeftHanded,
(!rh ? "" : "left-hand"), (!rh ? "left-hand" : ""));
Printf(false, "%FtTHROUGH%E %@ turns %Fl%Lt%D%f[change]%E",
g->valA, g->h.v, &ScreenChangeHelixParameter);
Printf(false, "%FtPITCH%E %s per turn %Fl%Lp%D%f[change]%E",
SS.MmToString(g->valB), g->h.v, &ScreenChangeHelixParameter);
Printf(false, "%FtdRADIUS%E %s per turn %Fl%Lr%D%f[change]%E",
SS.MmToString(g->valC), g->h.v, &ScreenChangeHelixParameter);
}
if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) {
bool space;
if(g->subtype == Group::ONE_SIDED) {
@ -733,10 +783,15 @@ void TextWindow::ShowGroupInfo(void) {
s2, times, times == 1 ? "" : "s",
g->h.v, &TextWindow::ScreenChangeExprA);
}
if(g->type == Group::IMPORTED) {
Printf(true, "%FtIMPORT%E '%s'", g->impFile);
}
if(g->type == Group::EXTRUDE ||
g->type == Group::LATHE ||
g->type == Group::SWEEP ||
g->type == Group::HELICAL_SWEEP ||
g->type == Group::IMPORTED)
{
bool un = (g->meshCombine == Group::COMBINE_AS_UNION);
@ -744,7 +799,7 @@ void TextWindow::ShowGroupInfo(void) {
bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE);
bool asa = (g->type == Group::IMPORTED);
Printf(false,
Printf((g->type == Group::HELICAL_SWEEP),
"%FtMERGE AS%E %Fh%f%D%Ll%s%E%Fs%s%E / %Fh%f%D%Ll%s%E%Fs%s%E %s "
"%Fh%f%D%Ll%s%E%Fs%s%E",
&TextWindow::ScreenChangeMeshCombine,
@ -764,7 +819,8 @@ void TextWindow::ShowGroupInfo(void) {
if(g->type == Group::EXTRUDE ||
g->type == Group::LATHE ||
g->type == Group::SWEEP)
g->type == Group::SWEEP ||
g->type == Group::HELICAL_SWEEP)
{
#define TWOX(v) v v
Printf(true, "%FtM_COLOR%E " TWOX(TWOX(TWOX("%Bp%D%f%Ln %Bd%E "))),
@ -997,6 +1053,27 @@ void TextWindow::EditControlDone(char *s) {
InvalidateGraphics();
break;
}
case EDIT_HELIX_TURNS:
case EDIT_HELIX_PITCH:
case EDIT_HELIX_DRADIUS: {
SS.UndoRemember();
Group *g = SS.GetGroup(edit.group);
Expr *e = Expr::From(s);
if(!e) {
Error("Not a valid number or expression: '%s'", s);
break;
}
if(edit.meaning == EDIT_HELIX_TURNS) {
g->valA = min(30, fabs(e->Eval()));
} else if(edit.meaning == EDIT_HELIX_PITCH) {
g->valB = SS.ExprToMm(e);
} else {
g->valC = SS.ExprToMm(e);
}
SS.MarkGroupDirty(g->h);
SS.later.generateAll = true;
break;
}
}
SS.later.showTW = true;
HideTextEditControl();

6
ui.h
View File

@ -64,6 +64,9 @@ public:
static const int EDIT_COLOR = 5;
static const int EDIT_MESH_TOLERANCE = 6;
static const int EDIT_CAMERA_TANGENT = 7;
static const int EDIT_HELIX_TURNS = 8;
static const int EDIT_HELIX_PITCH = 9;
static const int EDIT_HELIX_DRADIUS = 10;
struct {
int meaning;
int i;
@ -103,6 +106,8 @@ public:
static void ScreenChangeOneOrTwoSides(int link, DWORD v);
static void ScreenChangeSkipFirst(int link, DWORD v);
static void ScreenChangeMeshCombine(int link, DWORD v);
static void ScreenChangeRightLeftHanded(int link, DWORD v);
static void ScreenChangeHelixParameter(int link, DWORD v);
static void ScreenColor(int link, DWORD v);
static void ScreenShowConfiguration(int link, DWORD v);
@ -164,6 +169,7 @@ public:
MNU_GROUP_EXTRUDE,
MNU_GROUP_LATHE,
MNU_GROUP_SWEEP,
MNU_GROUP_HELICAL,
MNU_GROUP_ROT,
MNU_GROUP_TRANS,
MNU_GROUP_IMPORT,