diff --git a/CHANGELOG.md b/CHANGELOG.md index cd74efe0..932ff6d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Changelog --- New sketch features: - * New group, revolve. + * New groups, revolution and helical extrusion. * Extrude, lathe, translate and rotate groups can use the "assembly" boolean operation, to increase performance. * The solid model of extrude and lathe groups can be suppressed, diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 094b2438..ea53e9f2 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -88,6 +88,7 @@ void Entity::GetReferencePoints(std::vector *refs) { case Type::POINT_N_TRANS: case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_AA: + case Type::POINT_N_ROT_AXIS_TRANS: case Type::POINT_IN_3D: case Type::POINT_IN_2D: refs->push_back(PointGetNum()); @@ -502,6 +503,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) { case Type::POINT_N_TRANS: case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_AA: + case Type::POINT_N_ROT_AXIS_TRANS: case Type::POINT_IN_3D: case Type::POINT_IN_2D: { if(how == DrawAs::HIDDEN) return; diff --git a/src/entity.cpp b/src/entity.cpp index 06fba06a..930bb926 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -245,6 +245,7 @@ bool EntityBase::IsPoint() const { case Type::POINT_N_TRANS: case Type::POINT_N_ROT_TRANS: case Type::POINT_N_ROT_AA: + case Type::POINT_N_ROT_AXIS_TRANS: return true; default: @@ -454,10 +455,38 @@ void EntityBase::PointForceTo(Vector p) { // in order to avoid jumps when you cross from +pi to -pi while(dtheta < -PI) dtheta += 2*PI; while(dtheta > PI) dtheta -= 2*PI; + // this extra *2 explains the mystery *4 SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2); break; } + case Type::POINT_N_ROT_AXIS_TRANS: { + if(timesApplied == 0) break; + // is the point on the rotation axis? + Vector offset = Vector::From(param[0], param[1], param[2]); + Vector normal = Vector::From(param[4], param[5], param[6]).WithMagnitude(1.0); + Vector check = numPoint.Minus(offset).Cross(normal); + if (check.Dot(check) < LENGTH_EPS) { // if so, do extrusion style drag + Vector trans = (p.Minus(numPoint)); + SK.GetParam(param[7])->val = trans.Dot(normal)/timesApplied; + } else { // otherwise do rotation style + Vector u = normal.Normal(0), v = normal.Normal(1); + Vector po = p.Minus(offset), numo = numPoint.Minus(offset); + double thetap = atan2(v.Dot(po), u.Dot(po)); + double thetan = atan2(v.Dot(numo), u.Dot(numo)); + double thetaf = (thetap - thetan); + double thetai = (SK.GetParam(param[3])->val)*timesApplied*2; + double dtheta = thetaf - thetai; + // Take the smallest possible change in the actual step angle, + // in order to avoid jumps when you cross from +pi to -pi + while(dtheta < -PI) dtheta += 2*PI; + while(dtheta > PI) dtheta -= 2*PI; + // this extra *2 explains the mystery *4 + SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2); + } + break; + } + case Type::POINT_N_COPY: // Nothing to do; it's a static copy break; @@ -506,6 +535,17 @@ Vector EntityBase::PointGetNum() const { break; } + case Type::POINT_N_ROT_AXIS_TRANS: { + Vector offset = Vector::From(param[0], param[1], param[2]); + Vector displace = Vector::From(param[4], param[5], param[6]) + .WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied); + Quaternion q = PointGetQuaternion(); + p = numPoint.Minus(offset); + p = q.Rotate(p); + p = p.Plus(offset).Plus(displace); + break; + } + case Type::POINT_N_COPY: p = numPoint; break; @@ -555,6 +595,18 @@ ExprVector EntityBase::PointGetExprs() const { r = orig.Plus(trans); break; } + case Type::POINT_N_ROT_AXIS_TRANS: { + ExprVector orig = ExprVector::From(numPoint); + ExprVector trans = ExprVector::From(param[0], param[1], param[2]); + ExprVector displace = ExprVector::From(param[4], param[5], param[6]) + .WithMagnitude(Expr::From(1.0)).ScaledBy(Expr::From(timesApplied)).ScaledBy(Expr::From(param[7])); + + ExprQuaternion q = GetAxisAngleQuaternionExprs(3); + orig = orig.Minus(trans); + orig = q.Rotate(orig); + r = orig.Plus(trans).Plus(displace); + break; + } case Type::POINT_N_COPY: r = ExprVector::From(numPoint); break; @@ -633,7 +685,7 @@ ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) const { Quaternion EntityBase::PointGetQuaternion() const { Quaternion q; - if(type == Type::POINT_N_ROT_AA) { + if(type == Type::POINT_N_ROT_AA || type == Type::POINT_N_ROT_AXIS_TRANS) { q = GetAxisAngleQuaternion(3); } else if(type == Type::POINT_N_ROT_TRANS) { q = Quaternion::From(param[3], param[4], param[5], param[6]); @@ -807,7 +859,7 @@ bool EntityBase::IsInPlane(Vector norm, double distance) const { case Type::CIRCLE: case Type::ARC_OF_CIRCLE: { - // If it is an (arc of) a circle, check whether the normals + // If it is an (arc of) a circle, check whether the normals // are parallel and the mid point is in the plane. Vector n = Normal()->NormalN(); if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false; diff --git a/src/generate.cpp b/src/generate.cpp index d18017fa..d46d4f29 100644 --- a/src/generate.cpp +++ b/src/generate.cpp @@ -445,6 +445,7 @@ void SolveSpaceUI::MarkDraggedParams() { switch(pt->type) { case Entity::Type::POINT_N_TRANS: case Entity::Type::POINT_IN_3D: + case Entity::Type::POINT_N_ROT_AXIS_TRANS: sys.dragged.Add(&(pt->param[0])); sys.dragged.Add(&(pt->param[1])); sys.dragged.Add(&(pt->param[2])); diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index 8aa56bff..c851767c 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -105,6 +105,7 @@ const MenuEntry Menu[] = { { 1, N_("Step &Rotating"), Command::GROUP_ROT, S|'r', KN, mGrp }, { 1, NULL, Command::NONE, 0, KN, NULL }, { 1, N_("E&xtrude"), Command::GROUP_EXTRUDE, S|'x', KN, mGrp }, +{ 1, N_("&Helix"), Command::GROUP_HELIX, S|'h', KN, mGrp }, { 1, N_("&Lathe"), Command::GROUP_LATHE, S|'l', KN, mGrp }, { 1, N_("Re&volve"), Command::GROUP_REVOLVE, S|'v', KN, mGrp }, { 1, NULL, Command::NONE, 0, KN, NULL }, diff --git a/src/group.cpp b/src/group.cpp index d9a3bb2c..5d270b88 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -208,6 +208,30 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) { g.name = C_("group-name", "revolve"); break; + case Command::GROUP_HELIX: + if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) { + g.predef.origin = gs.point[0]; + g.predef.entityB = gs.vector[0]; + } else if(gs.lineSegments == 1 && gs.n == 1) { + g.predef.origin = SK.GetEntity(gs.entity[0])->point[0]; + g.predef.entityB = gs.entity[0]; + // since a line segment is a vector + } else { + Error(_("Bad selection for new helix group. This group can " + "be created with:\n\n" + " * a point and a line segment or normal " + "(revolved about an axis parallel to line / " + "normal, through point)\n" + " * a line segment (revolved about line segment)\n")); + return; + } + g.type = Type::HELIX; + g.opA = SS.GW.activeGroup; + g.valA = 2; + g.subtype = Subtype::ONE_SIDED; + g.name = C_("group-name", "helix"); + break; + case Command::GROUP_ROT: { if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) { g.predef.origin = gs.point[0]; @@ -368,7 +392,7 @@ std::string Group::DescriptionString() { void Group::Activate() { if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE || - type == Type::REVOLVE || type == Type::TRANSLATE || type == Type::ROTATE) { + type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) { SS.GW.showFaces = true; } else { SS.GW.showFaces = false; @@ -460,11 +484,11 @@ void Group::Generate(IdList *entity, // adds entities, which may cause a realloc. CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM, h.param(0), h.param(1), h.param(2), - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::N_TRANS); CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP, h.param(0), h.param(1), h.param(2), - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::N_TRANS); MakeExtrusionLines(entity, he); } @@ -492,17 +516,17 @@ void Group::Generate(IdList *entity, // adds entities, which may cause a realloc. CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM, NO_PARAM, - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_START, NO_PARAM, NO_PARAM, NO_PARAM, - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_END, NO_PARAM, NO_PARAM, NO_PARAM, - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai); @@ -527,7 +551,6 @@ void Group::Generate(IdList *entity, AddParam(param, h.param(5), axis_dir.y); AddParam(param, h.param(6), axis_dir.z); - int n = 2; int ai = 1; for(i = 0; i < entity->n; i++) { @@ -539,17 +562,17 @@ void Group::Generate(IdList *entity, hEntity he = e->h; CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM, - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); for(a = 0; a < 2; a++) { if(e->group != opA) continue; e->CalculateNumerical(false); - CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : (n - 1)), - (a == (n - 1)) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0), - h.param(1), h.param(2), h.param(3), h.param(4), h.param(5), - h.param(6), CopyAs::N_ROT_AA); + CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1), + (a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0), + h.param(1), h.param(2), h.param(3), h.param(4), h.param(5), + h.param(6), NO_PARAM, CopyAs::N_ROT_AA); } // Arcs are not generated for revolve groups, for now, because our current arc // entity is not chiral, and dragging a revolve may break the arc by inverting it. @@ -561,6 +584,63 @@ void Group::Generate(IdList *entity, return; } + case Type::HELIX: { + Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum(); + Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum(); + + // The center of rotation + AddParam(param, h.param(0), axis_pos.x); + AddParam(param, h.param(1), axis_pos.y); + AddParam(param, h.param(2), axis_pos.z); + // The rotation quaternion + AddParam(param, h.param(3), 30 * PI / 180); + AddParam(param, h.param(4), axis_dir.x); + AddParam(param, h.param(5), axis_dir.y); + AddParam(param, h.param(6), axis_dir.z); + // distance to translate along the rotation axis + AddParam(param, h.param(7), 20); + + int ai = 1; + + for(i = 0; i < entity->n; i++) { + Entity *e = &(entity->elem[i]); + if(e->group.v != opA.v) + continue; + + e->CalculateNumerical(/*forExport=*/false); + + CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC); + + for(a = 0; a < 2; a++) { + e->CalculateNumerical(false); + CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1), + (a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0), + h.param(1), h.param(2), h.param(3), h.param(4), h.param(5), + h.param(6), h.param(7), CopyAs::N_ROT_AXIS_TRANS); + } + // For point entities on the axis, create a construction line + if(e->IsPoint()) { + Vector check = e->PointGetNum().Minus(axis_pos).Cross(axis_dir); + if (check.Dot(check) < LENGTH_EPS) { + Entity *ep = SK.GetEntity(e->h); + Entity en = {}; + // A point gets extruded to form a line segment + en.point[0] = Remap(ep->h, REMAP_LATHE_START); + en.point[1] = Remap(ep->h, REMAP_LATHE_END); + en.group = h; + en.construction = ep->construction; + en.style = ep->style; + en.h = Remap(ep->h, REMAP_PT_TO_LINE); + en.type = Entity::Type::LINE_SEGMENT; + entity->Add(&en); + } + } + ai++; + } + return; + } + case Type::TRANSLATE: { // inherit meshCombine from source group Group *srcg = SK.GetGroup(opA); @@ -585,7 +665,7 @@ void Group::Generate(IdList *entity, a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)), (a == (n - 1)) ? REMAP_LAST : a, h.param(0), h.param(1), h.param(2), - NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, + NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::N_TRANS); } } @@ -620,7 +700,7 @@ void Group::Generate(IdList *entity, a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)), (a == (n - 1)) ? REMAP_LAST : a, h.param(0), h.param(1), h.param(2), - h.param(3), h.param(4), h.param(5), h.param(6), + h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM, CopyAs::N_ROT_AA); } } @@ -641,7 +721,7 @@ void Group::Generate(IdList *entity, Entity *ie = &(impEntity.elem[i]); CopyEntity(entity, ie, 0, 0, h.param(0), h.param(1), h.param(2), - h.param(3), h.param(4), h.param(5), h.param(6), + h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM, CopyAs::N_ROT_TRANS); } return; @@ -670,7 +750,7 @@ void Group::GenerateEquations(IdList *l) { Expr::From(h.param(5)), Expr::From(h.param(6)) }; AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0); - } else if(type == Type::ROTATE || type == Type::REVOLVE) { + } else if(type == Type::ROTATE || type == Type::REVOLVE || type == Type::HELIX) { // The axis and center of rotation are specified numerically #define EC(x) (Expr::From(x)) #define EP(x) (Expr::From(h.param(x))) @@ -867,7 +947,7 @@ void Group::MakeExtrusionTopBottomFaces(IdList *el, hEntity pt) void Group::CopyEntity(IdList *el, Entity *ep, int timesApplied, int remap, hParam dx, hParam dy, hParam dz, - hParam qw, hParam qvx, hParam qvy, hParam qvz, + hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist, CopyAs as) { Entity en = {}; @@ -891,6 +971,7 @@ void Group::CopyEntity(IdList *el, case Entity::Type::POINT_N_TRANS: case Entity::Type::POINT_N_ROT_TRANS: case Entity::Type::POINT_N_ROT_AA: + case Entity::Type::POINT_N_ROT_AXIS_TRANS: case Entity::Type::POINT_IN_3D: case Entity::Type::POINT_IN_2D: if(as == CopyAs::N_TRANS) { @@ -903,6 +984,8 @@ void Group::CopyEntity(IdList *el, } else { if(as == CopyAs::N_ROT_AA) { en.type = Entity::Type::POINT_N_ROT_AA; + } else if (as == CopyAs::N_ROT_AXIS_TRANS) { + en.type = Entity::Type::POINT_N_ROT_AXIS_TRANS; } else { en.type = Entity::Type::POINT_N_ROT_TRANS; } @@ -913,6 +996,9 @@ void Group::CopyEntity(IdList *el, en.param[4] = qvx; en.param[5] = qvy; en.param[6] = qvz; + if (as == CopyAs::N_ROT_AXIS_TRANS) { + en.param[7] = dist; + } } en.numPoint = (ep->actPoint).ScaledBy(scale); break; @@ -924,8 +1010,8 @@ void Group::CopyEntity(IdList *el, case Entity::Type::NORMAL_IN_2D: if(as == CopyAs::N_TRANS || as == CopyAs::NUMERIC) { en.type = Entity::Type::NORMAL_N_COPY; - } else { - if(as == CopyAs::N_ROT_AA) { + } else { // N_ROT_AXIS_TRANS probably doesn't warrant a new entity Type + if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) { en.type = Entity::Type::NORMAL_N_ROT_AA; } else { en.type = Entity::Type::NORMAL_N_ROT; @@ -960,7 +1046,7 @@ void Group::CopyEntity(IdList *el, } else if (as == CopyAs::NUMERIC) { en.type = Entity::Type::FACE_NORMAL_PT; } else { - if(as == CopyAs::N_ROT_AA) { + if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) { en.type = Entity::Type::FACE_N_ROT_AA; } else { en.type = Entity::Type::FACE_N_ROT_TRANS; diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index 4056ba2b..4079c8b4 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -296,6 +296,7 @@ void Group::GenerateShellAndMesh() { } else if(type == Type::REVOLVE && haveSrc) { Group *src = SK.GetGroup(opA); double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed? + double dists = 0, distf = 0; double angles = 0.0; if(subtype != Subtype::ONE_SIDED) { anglef *= 0.5; @@ -309,11 +310,34 @@ void Group::GenerateShellAndMesh() { SBezierLoopSet *sbls; for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { if(fabs(anglef - angles) < 2 * PI) { - thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this, angles, anglef); + thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this, + angles, anglef, dists, distf); } else { thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this); } } + } else if(type == Type::HELIX && haveSrc) { + Group *src = SK.GetGroup(opA); + double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed? + double dists = 0, distf = 0; + double angles = 0.0; + distf = SK.GetParam(h.param(7))->val * 2; // dist is applied twice + if(subtype != Subtype::ONE_SIDED) { + anglef *= 0.5; + angles = -anglef; + distf *= 0.5; + dists = -distf; + } + Vector pt = SK.GetEntity(predef.origin)->PointGetNum(), + axis = SK.GetEntity(predef.entityB)->VectorGetNum(); + axis = axis.WithMagnitude(1); + + SBezierLoopSetSet *sblss = &(src->bezierLoops); + SBezierLoopSet *sbls; + for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { + thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this, + angles, anglef, dists, distf); + } } else if(type == Type::LINKED) { // The imported shell or mesh are copied over, with the appropriate // transformation applied. We also must remap the face entities. @@ -488,6 +512,7 @@ bool Group::IsMeshGroup() { case Group::Type::EXTRUDE: case Group::Type::LATHE: case Group::Type::REVOLVE: + case Group::Type::HELIX: case Group::Type::ROTATE: case Group::Type::TRANSLATE: return true; diff --git a/src/sketch.h b/src/sketch.h index 292bff76..6cd4acaa 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -151,6 +151,7 @@ public: N_TRANS, N_ROT_AA, N_ROT_TRANS, + N_ROT_AXIS_TRANS, }; enum class Type : uint32_t { @@ -159,6 +160,7 @@ public: EXTRUDE = 5100, LATHE = 5101, REVOLVE = 5102, + HELIX = 5103, ROTATE = 5200, TRANSLATE = 5201, LINKED = 5300 @@ -286,7 +288,7 @@ public: void CopyEntity(EntityList *el, Entity *ep, int timesApplied, int remap, hParam dx, hParam dy, hParam dz, - hParam qw, hParam qvx, hParam qvy, hParam qvz, + hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist, CopyAs as); void AddEq(IdList *l, Expr *expr, int index); @@ -387,6 +389,7 @@ public: POINT_N_ROT_TRANS = 2011, POINT_N_COPY = 2012, POINT_N_ROT_AA = 2013, + POINT_N_ROT_AXIS_TRANS = 2014, NORMAL_IN_3D = 3000, NORMAL_IN_2D = 3001, @@ -426,7 +429,7 @@ public: hEntity distance; // The only types that have their own params are points, normals, // and directions. - hParam param[7]; + hParam param[8]; // Transformed points/normals/distances have their numerical base Vector numPoint; diff --git a/src/srf/surface.cpp b/src/srf/surface.cpp index 9bf28992..a6f7ac37 100644 --- a/src/srf/surface.cpp +++ b/src/srf/surface.cpp @@ -606,8 +606,8 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, Rgb } } -bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis) -// Check that the direction of revolution ends up parallel to the normal of +bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx) +// Check that the direction of revolution/extrusion ends up parallel to the normal of // the sketch, on the side of the axis where the sketch is. { SBezierLoop *sbl; @@ -631,9 +631,10 @@ bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector } } Vector ptc = pto.ClosestPointOnLine(pt, axis), - up = (pto.Minus(ptc)).WithMagnitude(1), - vp = (sbls->normal).Cross(up); - return (vp.Dot(axis) < 0); + up = axis.Cross(pto.Minus(ptc)).ScaledBy(da), + vp = up.Plus(axis.ScaledBy(dx)); + + return (vp.Dot(sbls->normal) > 0); } typedef struct { @@ -643,22 +644,19 @@ typedef struct { // sketch must not contain the axis of revolution as a non-construction line for helix void SShell::MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group, double angles, - double anglef) { + double anglef, double dists, double distf) { int i0 = surface.n; // number of pre-existing surfaces SBezierLoop *sbl; // for testing - hard code the axial distance, and number of sections. // distance will need to be parameters in the future. - double dist = 0; + double dist = distf - dists; int sections = fabs(anglef - angles) / (PI / 2) + 1; if(sections > 99) { sections = 99; } double wedge = (anglef - angles) / sections; - double dists = 0; // start distance - double distf = dist; // finish distance - - if(CheckNormalAxisRelationship(sbls, pt, axis) ^ (wedge < 0)) { + if(CheckNormalAxisRelationship(sbls, pt, axis, anglef-angles, distf-dists)) { swap(angles, anglef); swap(dists, distf); dist = -dist; @@ -821,7 +819,7 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, int i0 = surface.n; // number of pre-existing surfaces SBezierLoop *sbl; - if(CheckNormalAxisRelationship(sbls, pt, axis)) { + if(CheckNormalAxisRelationship(sbls, pt, axis, 1.0, 0.0)) { axis = axis.ScaledBy(-1); } diff --git a/src/srf/surface.h b/src/srf/surface.h index f61b87c1..99418c53 100644 --- a/src/srf/surface.h +++ b/src/srf/surface.h @@ -382,11 +382,11 @@ public: void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, RgbaColor color); - bool CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis); + bool CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx); void MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group); void MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, - Group *group, double angles, double anglef); + Group *group, double angles, double anglef, double dists, double distf); void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0); void MakeFromUnionOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b); diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 18f7f180..999001a4 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -304,11 +304,14 @@ void TextWindow::ShowGroupInfo() { if(g->type == Group::Type::LATHE) { Printf(true, " %Ftlathe plane sketch"); } else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE || - g->type == Group::Type::TRANSLATE || g->type == Group::Type::REVOLVE) { + g->type == Group::Type::TRANSLATE || g->type == Group::Type::REVOLVE || + g->type == Group::Type::HELIX) { if(g->type == Group::Type::EXTRUDE) { s = "extrude plane sketch"; } else if(g->type == Group::Type::TRANSLATE) { s = "translate original sketch"; + } else if(g->type == Group::Type::HELIX) { + s = "create helical extrusion"; } else if(g->type == Group::Type::ROTATE) { s = "rotate original sketch"; } else if(g->type == Group::Type::REVOLVE) { @@ -364,7 +367,8 @@ void TextWindow::ShowGroupInfo() { Printf(false, ""); if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE || - g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED) { + g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED || + g->type == Group::Type::HELIX) { bool un = (g->meshCombine == Group::CombineAs::UNION); bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE); bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE); @@ -384,7 +388,7 @@ void TextWindow::ShowGroupInfo() { (asy ? RADIO_TRUE : RADIO_FALSE)); if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE || - g->type == Group::Type::REVOLVE) { + g->type == Group::Type::REVOLVE || g->type == Group::Type::HELIX) { Printf(false, "%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", &g->color, @@ -396,7 +400,8 @@ void TextWindow::ShowGroupInfo() { } if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE || - g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED) { + g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED || + g->type == Group::Type::HELIX) { Printf(false, " %Fd%f%LP%s suppress this group's solid model", &TextWindow::ScreenChangeGroupOption, g->suppress ? CHECK_TRUE : CHECK_FALSE); diff --git a/src/ui.h b/src/ui.h index d9e78f3c..284e9d60 100644 --- a/src/ui.h +++ b/src/ui.h @@ -125,6 +125,7 @@ enum class Command : uint32_t { GROUP_3D, GROUP_WRKPL, GROUP_EXTRUDE, + GROUP_HELIX, GROUP_LATHE, GROUP_REVOLVE, GROUP_ROT,