Remove arbitrary limits on the selection size, and permit more than
one point to be dragged simultaneously. So now a dragged point drags all the selected points and entities, and a dragged entity drags its points (except for circles, which drag the radius). This means that the number of forced points for the solver must now be unlimited, and it is. Also add commands to invert the selection within the active group, and to select an edge chain starting from the current selection. And redo the context menus a bit; still not great, but less cluttered and more systematic. [git-p4: depot-paths = "//depot/solvespace/": change = 2064]
This commit is contained in:
parent
e74a655ffd
commit
b9ab62ab3f
76
draw.cpp
76
draw.cpp
@ -26,6 +26,12 @@ bool GraphicsWindow::Selection::IsStylable(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GraphicsWindow::Selection::HasEndpoints(void) {
|
||||
if(!entity.v) return false;
|
||||
Entity *e = SK.GetEntity(entity);
|
||||
return e->HasEndpoints();
|
||||
}
|
||||
|
||||
void GraphicsWindow::Selection::Clear(void) {
|
||||
entity.v = constraint.v = 0;
|
||||
emphasized = false;
|
||||
@ -64,67 +70,75 @@ void GraphicsWindow::Selection::Draw(void) {
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearSelection(void) {
|
||||
for(int i = 0; i < MAX_SELECTED; i++) {
|
||||
selection[i].Clear();
|
||||
}
|
||||
selection.Clear();
|
||||
SS.later.showTW = true;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearNonexistentSelectionItems(void) {
|
||||
bool change = false;
|
||||
for(int i = 0; i < MAX_SELECTED; i++) {
|
||||
Selection *s = &(selection[i]);
|
||||
Selection *s;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(s->constraint.v && !(SK.constraint.FindByIdNoOops(s->constraint))) {
|
||||
s->constraint.v = 0;
|
||||
s->tag = 1;
|
||||
change = true;
|
||||
}
|
||||
if(s->entity.v && !(SK.entity.FindByIdNoOops(s->entity))) {
|
||||
s->entity.v = 0;
|
||||
s->tag = 1;
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
selection.RemoveTagged();
|
||||
if(change) InvalidateGraphics();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Toggle the selection state of the hovered item: if it was selected then
|
||||
// Toggle the selection state of the indicated item: if it was selected then
|
||||
// un-select it, and if it wasn't then select it.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ToggleSelectionStateOfHovered(void) {
|
||||
if(hover.IsEmpty()) return;
|
||||
void GraphicsWindow::ToggleSelectionStateOf(hEntity he) {
|
||||
Selection stog;
|
||||
ZERO(&stog);
|
||||
stog.entity = he;
|
||||
ToggleSelectionStateOf(&stog);
|
||||
}
|
||||
void GraphicsWindow::ToggleSelectionStateOf(Selection *stog) {
|
||||
if(stog->IsEmpty()) return;
|
||||
|
||||
Selection *s;
|
||||
|
||||
// If an item was selected, then we just un-select it.
|
||||
int i;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].Equals(&hover)) {
|
||||
selection[i].Clear();
|
||||
bool wasSelected = false;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(s->Equals(stog)) {
|
||||
s->tag = 1;
|
||||
wasSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i != MAX_SELECTED) return;
|
||||
selection.RemoveTagged();
|
||||
if(wasSelected) return;
|
||||
|
||||
// So it's not selected, so we should select it.
|
||||
|
||||
if(hover.entity.v != 0 && SK.GetEntity(hover.entity)->IsFace()) {
|
||||
if(stog->entity.v != 0 && SK.GetEntity(stog->entity)->IsFace()) {
|
||||
// In the interest of speed for the triangle drawing code,
|
||||
// only two faces may be selected at a time.
|
||||
int c = 0;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
hEntity he = selection[i].entity;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
hEntity he = s->entity;
|
||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
||||
c++;
|
||||
if(c >= 2) selection[i].Clear();
|
||||
if(c >= 2) s->tag = 1;
|
||||
}
|
||||
}
|
||||
selection.RemoveTagged();
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].IsEmpty()) {
|
||||
selection[i] = hover;
|
||||
break;
|
||||
}
|
||||
}
|
||||
selection.Add(stog);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -135,8 +149,8 @@ void GraphicsWindow::ToggleSelectionStateOfHovered(void) {
|
||||
void GraphicsWindow::GroupSelection(void) {
|
||||
memset(&gs, 0, sizeof(gs));
|
||||
int i;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
Selection *s = &(selection[i]);
|
||||
for(i = 0; i < selection.n && i < MAX_SELECTED; i++) {
|
||||
Selection *s = &(selection.elem[i]);
|
||||
if(s->entity.v) {
|
||||
(gs.n)++;
|
||||
|
||||
@ -167,6 +181,10 @@ void GraphicsWindow::GroupSelection(void) {
|
||||
gs.face[(gs.faces)++] = s->entity;
|
||||
}
|
||||
|
||||
if(e->HasEndpoints()) {
|
||||
(gs.withEndpoints)++;
|
||||
}
|
||||
|
||||
// And some aux counts too
|
||||
switch(e->type) {
|
||||
case Entity::WORKPLANE: (gs.workplanes)++; break;
|
||||
@ -533,8 +551,8 @@ nogrid:;
|
||||
|
||||
// And finally draw the selection, same mechanism.
|
||||
glxLockColorTo(Style::Color(Style::SELECTED));
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
selection[i].Draw();
|
||||
for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
s->Draw();
|
||||
}
|
||||
|
||||
glxUnlockColor();
|
||||
|
32
entity.cpp
32
entity.cpp
@ -157,6 +157,10 @@ void EntityBase::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) {
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityBase::IsDistance(void) {
|
||||
return (type == DISTANCE) ||
|
||||
(type == DISTANCE_N_COPY);
|
||||
}
|
||||
double EntityBase::DistanceGetNum(void) {
|
||||
if(type == DISTANCE) {
|
||||
return SK.GetParam(param[0])->val;
|
||||
@ -679,6 +683,34 @@ Vector EntityBase::FaceGetPointNum(void) {
|
||||
return r;
|
||||
}
|
||||
|
||||
bool EntityBase::HasEndpoints(void) {
|
||||
return (type == LINE_SEGMENT) ||
|
||||
(type == CUBIC) ||
|
||||
(type == ARC_OF_CIRCLE);
|
||||
}
|
||||
Vector EntityBase::EndpointStart() {
|
||||
if(type == LINE_SEGMENT) {
|
||||
return SK.GetEntity(point[0])->PointGetNum();
|
||||
} else if(type == CUBIC) {
|
||||
return CubicGetStartNum();
|
||||
} else if(type == ARC_OF_CIRCLE) {
|
||||
return SK.GetEntity(point[1])->PointGetNum();
|
||||
} else {
|
||||
oops();
|
||||
}
|
||||
}
|
||||
Vector EntityBase::EndpointFinish() {
|
||||
if(type == LINE_SEGMENT) {
|
||||
return SK.GetEntity(point[1])->PointGetNum();
|
||||
} else if(type == CUBIC) {
|
||||
return CubicGetFinishNum();
|
||||
} else if(type == ARC_OF_CIRCLE) {
|
||||
return SK.GetEntity(point[2])->PointGetNum();
|
||||
} else {
|
||||
oops();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
|
||||
Equation eq;
|
||||
eq.e = expr;
|
||||
|
@ -181,14 +181,12 @@ default: dbp("bad constraint type %d", sc->type); return;
|
||||
SK.constraint.Add(&c);
|
||||
}
|
||||
|
||||
if(System::MAX_DRAGGED < 4) oops();
|
||||
for(i = 0; i < System::MAX_DRAGGED; i++) {
|
||||
SYS.dragged[i].v = 0;
|
||||
for(i = 0; i < arraylen(ssys->dragged); i++) {
|
||||
if(ssys->dragged[i]) {
|
||||
hParam hp = { ssys->dragged[i] };
|
||||
SYS.dragged.Add(&hp);
|
||||
}
|
||||
}
|
||||
SYS.dragged[0].v = ssys->dragged[0];
|
||||
SYS.dragged[1].v = ssys->dragged[1];
|
||||
SYS.dragged[2].v = ssys->dragged[2];
|
||||
SYS.dragged[3].v = ssys->dragged[3];
|
||||
|
||||
Group g;
|
||||
ZERO(&g);
|
||||
@ -240,6 +238,7 @@ default: dbp("bad constraint type %d", sc->type); return;
|
||||
SYS.param.Clear();
|
||||
SYS.entity.Clear();
|
||||
SYS.eq.Clear();
|
||||
SYS.dragged.Clear();
|
||||
|
||||
SK.param.Clear();
|
||||
SK.entity.Clear();
|
||||
|
37
generate.cpp
37
generate.cpp
@ -381,28 +381,33 @@ void SolveSpace::ForceReferences(void) {
|
||||
}
|
||||
|
||||
void SolveSpace::MarkDraggedParams(void) {
|
||||
int i;
|
||||
for(i = 0; i < System::MAX_DRAGGED; i++) {
|
||||
sys.dragged[i] = Param::NO_PARAM;
|
||||
}
|
||||
sys.dragged.Clear();
|
||||
|
||||
for(int i = -1; i < SS.GW.pending.points.n; i++) {
|
||||
hEntity hp;
|
||||
if(i == -1) {
|
||||
hp = SS.GW.pending.point;
|
||||
} else {
|
||||
hp = SS.GW.pending.points.elem[i];
|
||||
}
|
||||
if(!hp.v) continue;
|
||||
|
||||
if(SS.GW.pending.point.v) {
|
||||
// The pending point could be one in a group that has not yet
|
||||
// been processed, in which case the lookup will fail; but
|
||||
// that's not an error.
|
||||
Entity *pt = SK.entity.FindByIdNoOops(SS.GW.pending.point);
|
||||
Entity *pt = SK.entity.FindByIdNoOops(hp);
|
||||
if(pt) {
|
||||
switch(pt->type) {
|
||||
case Entity::POINT_N_TRANS:
|
||||
case Entity::POINT_IN_3D:
|
||||
sys.dragged[0] = pt->param[0];
|
||||
sys.dragged[1] = pt->param[1];
|
||||
sys.dragged[2] = pt->param[2];
|
||||
sys.dragged.Add(&(pt->param[0]));
|
||||
sys.dragged.Add(&(pt->param[1]));
|
||||
sys.dragged.Add(&(pt->param[2]));
|
||||
break;
|
||||
|
||||
case Entity::POINT_IN_2D:
|
||||
sys.dragged[0] = pt->param[0];
|
||||
sys.dragged[1] = pt->param[1];
|
||||
sys.dragged.Add(&(pt->param[0]));
|
||||
sys.dragged.Add(&(pt->param[1]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -413,7 +418,7 @@ void SolveSpace::MarkDraggedParams(void) {
|
||||
Entity *dist = SK.GetEntity(circ->distance);
|
||||
switch(dist->type) {
|
||||
case Entity::DISTANCE:
|
||||
sys.dragged[0] = dist->param[0];
|
||||
sys.dragged.Add(&(dist->param[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -423,10 +428,10 @@ void SolveSpace::MarkDraggedParams(void) {
|
||||
if(norm) {
|
||||
switch(norm->type) {
|
||||
case Entity::NORMAL_IN_3D:
|
||||
sys.dragged[0] = norm->param[0];
|
||||
sys.dragged[1] = norm->param[1];
|
||||
sys.dragged[2] = norm->param[2];
|
||||
sys.dragged[3] = norm->param[3];
|
||||
sys.dragged.Add(&(norm->param[0]));
|
||||
sys.dragged.Add(&(norm->param[1]));
|
||||
sys.dragged.Add(&(norm->param[2]));
|
||||
sys.dragged.Add(&(norm->param[3]));
|
||||
break;
|
||||
// other types are locked, so not draggable
|
||||
}
|
||||
|
@ -35,8 +35,14 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Snap Selection to &Grid\t.", MNU_SNAP_TO_GRID, '.', mEdit },
|
||||
{ 1, "Rotate Imported &90°\t9", MNU_ROTATE_90, '9', mEdit },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mEdit },
|
||||
{ 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|C, mEdit },
|
||||
{ 1, "&Paste\tCtrl+V", MNU_PASTE, 'V'|C, mEdit },
|
||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Select Edge Cha&in\tCtrl+I", MNU_SELECT_CHAIN, 'I'|C, mEdit },
|
||||
{ 1, "Invert &Selection\tCtrl+A", MNU_INVERT_SEL, 'A'|C, mEdit },
|
||||
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
||||
|
||||
{ 0, "&View", 0, NULL },
|
||||
@ -618,14 +624,75 @@ void GraphicsWindow::MenuEdit(int id) {
|
||||
SS.nakedEdges.Clear();
|
||||
break;
|
||||
|
||||
case MNU_INVERT_SEL: {
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
||||
if(e->IsFace() || e->IsDistance()) continue;
|
||||
|
||||
SS.GW.ToggleSelectionStateOf(e->h);
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_SELECT_CHAIN: {
|
||||
Entity *e;
|
||||
int newlySelected = 0;
|
||||
bool didSomething;
|
||||
do {
|
||||
didSomething = false;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
||||
if(!e->HasEndpoints()) continue;
|
||||
|
||||
Vector st = e->EndpointStart(),
|
||||
fi = e->EndpointFinish();
|
||||
|
||||
bool onChain = false, alreadySelected = false;
|
||||
List<Selection> *ls = &(SS.GW.selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
if(s->entity.v == e->h.v) {
|
||||
alreadySelected = true;
|
||||
continue;
|
||||
}
|
||||
Entity *se = SK.GetEntity(s->entity);
|
||||
if(!se->HasEndpoints()) continue;
|
||||
|
||||
Vector sst = se->EndpointStart(),
|
||||
sfi = se->EndpointFinish();
|
||||
|
||||
if(sst.Equals(st) || sst.Equals(fi) ||
|
||||
sfi.Equals(st) || sfi.Equals(fi))
|
||||
{
|
||||
onChain = true;
|
||||
}
|
||||
}
|
||||
if(onChain && !alreadySelected) {
|
||||
SS.GW.ToggleSelectionStateOf(e->h);
|
||||
newlySelected++;
|
||||
didSomething = true;
|
||||
}
|
||||
}
|
||||
} while(didSomething);
|
||||
if(newlySelected == 0) {
|
||||
Error("No entities share endpoints with the selected "
|
||||
"entities.");
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_DELETE: {
|
||||
SS.UndoRemember();
|
||||
|
||||
int i;
|
||||
SK.request.ClearTags();
|
||||
SK.constraint.ClearTags();
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
Selection *s = &(SS.GW.selection[i]);
|
||||
List<Selection> *ls = &(SS.GW.selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
hRequest r; r.v = 0;
|
||||
if(s->entity.v && s->entity.isFromRequest()) {
|
||||
r = s->entity.request();
|
||||
@ -702,14 +769,14 @@ void GraphicsWindow::MenuEdit(int id) {
|
||||
Vector p = ep->PointGetNum();
|
||||
|
||||
ep->PointForceTo(SS.GW.SnapToGrid(p));
|
||||
|
||||
// Regenerate, with this point marked as dragged so that it
|
||||
// gets placed as close as possible to our snap
|
||||
SS.GW.pending.point = hp;
|
||||
SS.GW.pending.points.Add(&hp);
|
||||
SS.MarkGroupDirty(ep->group);
|
||||
SS.GenerateAll();
|
||||
SS.GW.pending.point = Entity::NO_ENTITY;
|
||||
}
|
||||
// Regenerate, with these points marked as dragged so that they
|
||||
// get placed as close as possible to our snap grid.
|
||||
SS.GenerateAll();
|
||||
SS.GW.ClearPending();
|
||||
|
||||
for(i = 0; i < SS.GW.gs.constraints; i++) {
|
||||
Constraint *c = SK.GetConstraint(SS.GW.gs.constraint[i]);
|
||||
c->disp.offset = SS.GW.SnapToGrid(c->disp.offset);
|
||||
|
268
mouse.cpp
268
mouse.cpp
@ -13,10 +13,64 @@ void GraphicsWindow::UpdateDraggedPoint(hEntity hp, double mx, double my) {
|
||||
void GraphicsWindow::UpdateDraggedNum(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;
|
||||
InvalidateGraphics();
|
||||
void GraphicsWindow::AddPointToDraggedList(hEntity hp) {
|
||||
Entity *p = SK.GetEntity(hp);
|
||||
// If an entity and its points are both selected, then its points could
|
||||
// end up in the list twice. This would be bad, because it would move
|
||||
// twice as far as the mouse pointer...
|
||||
List<hEntity> *lhe = &(pending.points);
|
||||
for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) {
|
||||
if(hee->v == hp.v) {
|
||||
// Exact same point.
|
||||
return;
|
||||
}
|
||||
Entity *pe = SK.GetEntity(*hee);
|
||||
if(pe->type == p->type &&
|
||||
pe->type != Entity::POINT_IN_2D &&
|
||||
pe->type != Entity::POINT_IN_3D &&
|
||||
pe->group.v == p->group.v)
|
||||
{
|
||||
// Transform-type point, from the same group. So it handles the
|
||||
// same unknowns.
|
||||
return;
|
||||
}
|
||||
}
|
||||
pending.points.Add(&hp);
|
||||
}
|
||||
|
||||
void GraphicsWindow::StartDraggingByEntity(hEntity he) {
|
||||
Entity *e = SK.GetEntity(he);
|
||||
if(e->IsPoint()) {
|
||||
AddPointToDraggedList(e->h);
|
||||
} else if(e->type == Entity::LINE_SEGMENT ||
|
||||
e->type == Entity::ARC_OF_CIRCLE ||
|
||||
e->type == Entity::CUBIC ||
|
||||
e->type == Entity::CUBIC_PERIODIC ||
|
||||
e->type == Entity::CIRCLE ||
|
||||
e->type == Entity::TTF_TEXT)
|
||||
{
|
||||
int pts;
|
||||
EntReqTable::GetEntityInfo(e->type, e->extraPoints,
|
||||
NULL, &pts, NULL, NULL);
|
||||
for(int i = 0; i < pts; i++) {
|
||||
AddPointToDraggedList(e->point[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::StartDraggingBySelection(void) {
|
||||
List<Selection> *ls = &(selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
|
||||
StartDraggingByEntity(s->entity);
|
||||
}
|
||||
// The user might select a point, and then click it again to start
|
||||
// dragging; but the point just got unselected by that click. So drag
|
||||
// the hovered item too, and they'll always have it.
|
||||
if(hover.entity.v) StartDraggingByEntity(hover.entity);
|
||||
}
|
||||
|
||||
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
@ -37,7 +91,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
}
|
||||
}
|
||||
|
||||
if(!leftDown && pending.operation == DRAGGING_POINT) {
|
||||
if(!leftDown && pending.operation == DRAGGING_POINTS) {
|
||||
ClearPending();
|
||||
}
|
||||
|
||||
@ -96,12 +150,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
if(leftDown && dm > 3) {
|
||||
if(hover.entity.v) {
|
||||
Entity *e = SK.GetEntity(hover.entity);
|
||||
if(e->IsPoint()) {
|
||||
// Start dragging this point.
|
||||
ClearSelection();
|
||||
pending.point = hover.entity;
|
||||
pending.operation = DRAGGING_POINT;
|
||||
} else if(e->type == Entity::CIRCLE) {
|
||||
if(e->type == Entity::CIRCLE) {
|
||||
// Drag the radius.
|
||||
ClearSelection();
|
||||
pending.circle = hover.entity;
|
||||
@ -110,6 +159,11 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
ClearSelection();
|
||||
pending.normal = hover.entity;
|
||||
pending.operation = DRAGGING_NORMAL;
|
||||
} else {
|
||||
StartDraggingBySelection();
|
||||
ClearSelection();
|
||||
hover.Clear();
|
||||
pending.operation = DRAGGING_POINTS;
|
||||
}
|
||||
} else if(hover.constraint.v &&
|
||||
SK.GetConstraint(hover.constraint)->HasLabel())
|
||||
@ -149,26 +203,28 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
case DRAGGING_CONSTRAINT: {
|
||||
Constraint *c = SK.constraint.FindById(pending.constraint);
|
||||
UpdateDraggedNum(&(c->disp.offset), x, y);
|
||||
orig.mouse = mp;
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
case DRAGGING_NEW_LINE_POINT:
|
||||
HitTestMakeSelection(mp);
|
||||
// and fall through
|
||||
case DRAGGING_NEW_POINT:
|
||||
case DRAGGING_POINT: {
|
||||
Entity *p = SK.GetEntity(pending.point);
|
||||
if((p->type == Entity::POINT_N_ROT_TRANS) &&
|
||||
(shiftDown || ctrlDown))
|
||||
{
|
||||
// These points also come with a rotation, which the user can
|
||||
// edit by pressing shift or control.
|
||||
Quaternion q = p->PointGetQuaternion();
|
||||
Vector p3 = p->PointGetNum();
|
||||
Point2d p2 = ProjectPoint(p3);
|
||||
|
||||
Vector u = q.RotationU(), v = q.RotationV();
|
||||
case DRAGGING_NEW_LINE_POINT:
|
||||
case DRAGGING_NEW_POINT:
|
||||
UpdateDraggedPoint(pending.point, x, y);
|
||||
HitTestMakeSelection(mp);
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
orig.mouse = mp;
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case DRAGGING_POINTS:
|
||||
if(shiftDown || ctrlDown) {
|
||||
// Edit the rotation associated with a POINT_N_ROT_TRANS,
|
||||
// either within (ctrlDown) or out of (shiftDown) the plane
|
||||
// of the screen. So first get the rotation to apply, in qt.
|
||||
Quaternion qt;
|
||||
if(ctrlDown) {
|
||||
double d = mp.DistanceTo(p2);
|
||||
double d = mp.DistanceTo(orig.mouseOnButtonDown);
|
||||
if(d < 25) {
|
||||
// Don't start dragging the position about the normal
|
||||
// until we're a little ways out, to get a reasonable
|
||||
@ -176,34 +232,47 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
orig.mouse = mp;
|
||||
break;
|
||||
}
|
||||
double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x);
|
||||
theta -= atan2(y-p2.y, x-p2.x);
|
||||
double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y,
|
||||
orig.mouse.x-orig.mouseOnButtonDown.x);
|
||||
theta -= atan2(y-orig.mouseOnButtonDown.y,
|
||||
x-orig.mouseOnButtonDown.x);
|
||||
|
||||
Vector gn = projRight.Cross(projUp);
|
||||
u = u.RotatedAbout(gn, -theta);
|
||||
v = v.RotatedAbout(gn, -theta);
|
||||
qt = Quaternion::From(gn, -theta);
|
||||
} else {
|
||||
double dx = -(x - orig.mouse.x);
|
||||
double dy = -(y - orig.mouse.y);
|
||||
double s = 0.3*(PI/180); // degrees per pixel
|
||||
u = u.RotatedAbout(projUp, -s*dx);
|
||||
u = u.RotatedAbout(projRight, s*dy);
|
||||
v = v.RotatedAbout(projUp, -s*dx);
|
||||
v = v.RotatedAbout(projRight, s*dy);
|
||||
qt = Quaternion::From(projUp, -s*dx).Times(
|
||||
Quaternion::From(projRight, s*dy));
|
||||
}
|
||||
q = Quaternion::From(u, v);
|
||||
p->PointForceQuaternionTo(q);
|
||||
orig.mouse = mp;
|
||||
|
||||
// Now apply this rotation to the points being dragged.
|
||||
List<hEntity> *lhe = &(pending.points);
|
||||
for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
|
||||
Entity *e = SK.GetEntity(*he);
|
||||
if(e->type != Entity::POINT_N_ROT_TRANS) continue;
|
||||
|
||||
Quaternion q = e->PointGetQuaternion();
|
||||
Vector p = e->PointGetNum();
|
||||
q = qt.Times(q);
|
||||
e->PointForceQuaternionTo(q);
|
||||
// Let's rotate about the selected point; so fix up the
|
||||
// translation so that that point didn't move.
|
||||
p->PointForceTo(p3);
|
||||
orig.mouse = mp;
|
||||
e->PointForceTo(p);
|
||||
SS.MarkGroupDirtyByEntity(e->h);
|
||||
}
|
||||
} else {
|
||||
UpdateDraggedPoint(pending.point, x, y);
|
||||
HitTestMakeSelection(mp);
|
||||
List<hEntity> *lhe = &(pending.points);
|
||||
for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
|
||||
UpdateDraggedPoint(*he, x, y);
|
||||
SS.MarkGroupDirtyByEntity(*he);
|
||||
}
|
||||
orig.mouse = mp;
|
||||
}
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
|
||||
case DRAGGING_NEW_CUBIC_POINT: {
|
||||
UpdateDraggedPoint(pending.point, x, y);
|
||||
HitTestMakeSelection(mp);
|
||||
@ -228,6 +297,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
SK.GetEntity(hr.entity(3+i))->PointForceTo(pnm1);
|
||||
}
|
||||
|
||||
orig.mouse = mp;
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
@ -242,6 +312,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
|
||||
SK.GetEntity(hr.entity(1))->PointForceTo(center);
|
||||
|
||||
orig.mouse = mp;
|
||||
SS.MarkGroupDirtyByEntity(pending.point);
|
||||
break;
|
||||
}
|
||||
@ -297,7 +368,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearPending(void) {
|
||||
memset(&pending, 0, sizeof(pending));
|
||||
pending.points.Clear();
|
||||
ZERO(&pending);
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
@ -359,105 +431,99 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||
// or on the hovered item. In the latter case we can fudge things by just
|
||||
// selecting the hovered item, and then applying our operation to the
|
||||
// selection.
|
||||
bool toggleForStyles = false,
|
||||
toggleForGroupInfo = false,
|
||||
toggleForDelete = false,
|
||||
toggleForStyleInfo = false;
|
||||
bool selEmpty = false;
|
||||
if(gs.n == 0 && gs.constraints == 0) {
|
||||
selEmpty = true;
|
||||
}
|
||||
|
||||
if(selEmpty) {
|
||||
if(hover.IsStylable()) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Hovered: Assign to Style", CONTEXT_SUBMENU);
|
||||
}
|
||||
if(!hover.IsEmpty()) {
|
||||
AddContextMenuItem("Toggle Hovered Item Selection",
|
||||
CMNU_TOGGLE_SELECTION);
|
||||
AddContextMenuItem("Hovered: Group Info", CMNU_GROUP_INFO);
|
||||
}
|
||||
|
||||
if(gs.stylables > 0) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Assign Selection to Style", CONTEXT_SUBMENU);
|
||||
} else if(gs.n == 0 && gs.constraints == 0 && hover.IsStylable()) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Assign Hovered Item to Style", CONTEXT_SUBMENU);
|
||||
toggleForStyles = true;
|
||||
if(hover.IsStylable()) {
|
||||
AddContextMenuItem("Hovered: Style Info", CMNU_STYLE_INFO);
|
||||
}
|
||||
|
||||
if(gs.n + gs.constraints == 1) {
|
||||
AddContextMenuItem("Group Info for Selected Item", CMNU_GROUP_INFO);
|
||||
} else if(!hover.IsEmpty() && gs.n == 0 && gs.constraints == 0) {
|
||||
AddContextMenuItem("Group Info for Hovered Item", CMNU_GROUP_INFO);
|
||||
toggleForGroupInfo = true;
|
||||
}
|
||||
|
||||
if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
|
||||
AddContextMenuItem("Style Info for Selected Item", CMNU_STYLE_INFO);
|
||||
} else if(hover.IsStylable() && gs.n == 0 && gs.constraints == 0) {
|
||||
AddContextMenuItem("Style Info for Hovered Item", CMNU_STYLE_INFO);
|
||||
toggleForStyleInfo = true;
|
||||
}
|
||||
|
||||
if(hover.constraint.v && gs.n == 0 && gs.constraints == 0) {
|
||||
if(hover.constraint.v) {
|
||||
Constraint *c = SK.GetConstraint(hover.constraint);
|
||||
if(c->HasLabel() && c->type != Constraint::COMMENT) {
|
||||
AddContextMenuItem("Toggle Reference Dimension",
|
||||
AddContextMenuItem("Hovered: Toggle Reference Dimension",
|
||||
CMNU_REFERENCE_DIM);
|
||||
}
|
||||
if(c->type == Constraint::ANGLE ||
|
||||
c->type == Constraint::EQUAL_ANGLE)
|
||||
{
|
||||
AddContextMenuItem("Other Supplementary Angle",
|
||||
AddContextMenuItem("Hovered: Other Supplementary Angle",
|
||||
CMNU_OTHER_ANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
if(gs.n == 0 && gs.constraints == 0 &&
|
||||
(hover.constraint.v &&
|
||||
if(hover.HasEndpoints()) {
|
||||
AddContextMenuItem("Hovered: Select Edge Chain", CMNU_SELECT_CHAIN);
|
||||
}
|
||||
if((hover.constraint.v &&
|
||||
SK.GetConstraint(hover.constraint)->type == Constraint::COMMENT) ||
|
||||
(hover.entity.v &&
|
||||
SK.GetEntity(hover.entity)->IsPoint()))
|
||||
{
|
||||
AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID);
|
||||
AddContextMenuItem("Hovered: Snap to Grid", CMNU_SNAP_TO_GRID);
|
||||
}
|
||||
if(!hover.IsEmpty()) {
|
||||
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
AddContextMenuItem("Delete Hovered Item", CMNU_DELETE_SEL);
|
||||
}
|
||||
} else {
|
||||
if(gs.stylables > 0) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Selected: Assign to Style", CONTEXT_SUBMENU);
|
||||
}
|
||||
if(gs.n + gs.constraints == 1) {
|
||||
AddContextMenuItem("Selected: Group Info", CMNU_GROUP_INFO);
|
||||
}
|
||||
if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
|
||||
AddContextMenuItem("Selected: Style Info", CMNU_STYLE_INFO);
|
||||
}
|
||||
if(gs.withEndpoints > 0) {
|
||||
AddContextMenuItem("Selected: Select Edge Chain",
|
||||
CMNU_SELECT_CHAIN);
|
||||
}
|
||||
|
||||
if(gs.n > 0 || gs.constraints > 0) {
|
||||
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
AddContextMenuItem("Delete Selection", CMNU_DELETE_SEL);
|
||||
AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL);
|
||||
} else if(!hover.IsEmpty()) {
|
||||
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
AddContextMenuItem("Delete Hovered Item", CMNU_DELETE_SEL);
|
||||
toggleForDelete = true;
|
||||
}
|
||||
|
||||
int ret = ShowContextMenu();
|
||||
if(ret != 0 && selEmpty) {
|
||||
ToggleSelectionStateOf(&hover);
|
||||
}
|
||||
switch(ret) {
|
||||
case CMNU_TOGGLE_SELECTION:
|
||||
ToggleSelectionStateOfHovered();
|
||||
break;
|
||||
|
||||
case CMNU_UNSELECT_ALL:
|
||||
MenuEdit(MNU_UNSELECT_ALL);
|
||||
break;
|
||||
|
||||
case CMNU_SELECT_CHAIN:
|
||||
MenuEdit(MNU_SELECT_CHAIN);
|
||||
break;
|
||||
|
||||
case CMNU_DELETE_SEL:
|
||||
if(toggleForDelete) ToggleSelectionStateOfHovered();
|
||||
MenuEdit(MNU_DELETE);
|
||||
break;
|
||||
|
||||
case CMNU_REFERENCE_DIM:
|
||||
ToggleSelectionStateOfHovered();
|
||||
Constraint::MenuConstrain(MNU_REFERENCE);
|
||||
break;
|
||||
|
||||
case CMNU_OTHER_ANGLE:
|
||||
ToggleSelectionStateOfHovered();
|
||||
Constraint::MenuConstrain(MNU_OTHER_ANGLE);
|
||||
break;
|
||||
|
||||
case CMNU_SNAP_TO_GRID:
|
||||
ToggleSelectionStateOfHovered();
|
||||
MenuEdit(MNU_SNAP_TO_GRID);
|
||||
break;
|
||||
|
||||
case CMNU_GROUP_INFO: {
|
||||
if(toggleForGroupInfo) ToggleSelectionStateOfHovered();
|
||||
|
||||
hGroup hg;
|
||||
GroupSelection();
|
||||
if(gs.entities == 1) {
|
||||
@ -478,8 +544,6 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||
}
|
||||
|
||||
case CMNU_STYLE_INFO: {
|
||||
if(toggleForStyleInfo) ToggleSelectionStateOfHovered();
|
||||
|
||||
hStyle hs;
|
||||
GroupSelection();
|
||||
if(gs.entities == 1) {
|
||||
@ -501,23 +565,20 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||
}
|
||||
|
||||
case CMNU_NEW_CUSTOM_STYLE: {
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
DWORD v = Style::CreateCustomStyle();
|
||||
Style::AssignSelectionToStyle(v);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMNU_NO_STYLE:
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
Style::AssignSelectionToStyle(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(ret >= CMNU_FIRST_STYLE) {
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE);
|
||||
}
|
||||
// otherwise it was probably cancelled, so do nothing
|
||||
// otherwise it was cancelled, so do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
@ -587,6 +648,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||
MouseMoved(mx, my, false, false, false, false, false);
|
||||
orig.mouse.x = mx;
|
||||
orig.mouse.y = my;
|
||||
orig.mouseOnButtonDown = orig.mouse;
|
||||
|
||||
// The current mouse location
|
||||
Vector v = offset.ScaledBy(-1);
|
||||
@ -837,7 +899,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||
case 0:
|
||||
default:
|
||||
ClearPending();
|
||||
ToggleSelectionStateOfHovered();
|
||||
ToggleSelectionStateOf(&hover);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -847,7 +909,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||
|
||||
void GraphicsWindow::MouseLeftUp(double mx, double my) {
|
||||
switch(pending.operation) {
|
||||
case DRAGGING_POINT:
|
||||
case DRAGGING_POINTS:
|
||||
case DRAGGING_CONSTRAINT:
|
||||
case DRAGGING_NORMAL:
|
||||
case DRAGGING_RADIUS:
|
||||
|
5
sketch.h
5
sketch.h
@ -356,6 +356,7 @@ public:
|
||||
Vector VectorGetRefPoint(void);
|
||||
|
||||
// For distances
|
||||
bool IsDistance(void);
|
||||
double DistanceGetNum(void);
|
||||
Expr *DistanceGetExpr(void);
|
||||
void DistanceForceTo(double v);
|
||||
@ -401,6 +402,10 @@ public:
|
||||
ExprVector CubicGetStartTangentExprs(void);
|
||||
ExprVector CubicGetFinishTangentExprs(void);
|
||||
|
||||
bool HasEndpoints(void);
|
||||
Vector EndpointStart();
|
||||
Vector EndpointFinish();
|
||||
|
||||
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
|
||||
void GenerateEquations(IdList<Equation,hEquation> *l);
|
||||
};
|
||||
|
@ -238,7 +238,6 @@ bool StringEndsIn(char *str, char *ending);
|
||||
class System {
|
||||
public:
|
||||
static const int MAX_UNKNOWNS = 1024;
|
||||
static const int MAX_DRAGGED = 4;
|
||||
|
||||
EntityList entity;
|
||||
ParamList param;
|
||||
@ -246,7 +245,7 @@ public:
|
||||
|
||||
// A list of parameters that are being dragged; these are the ones that
|
||||
// we should put as close as possible to their initial positions.
|
||||
hParam dragged[MAX_DRAGGED];
|
||||
List<hParam> dragged;
|
||||
|
||||
// In general, the tag indicates the subsys that a variable/equation
|
||||
// has been assigned to; these are exceptions for variables:
|
||||
|
@ -61,9 +61,9 @@ void System::EvalJacobian(void) {
|
||||
}
|
||||
|
||||
bool System::IsDragged(hParam p) {
|
||||
int i;
|
||||
for(i = 0; i < MAX_DRAGGED; i++) {
|
||||
if(p.v == dragged[i].v) return true;
|
||||
hParam *pp;
|
||||
for(pp = dragged.First(); pp; pp = dragged.NextAfter(pp)) {
|
||||
if(p.v == pp->v) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -196,12 +196,18 @@ void TextWindow::ScreenHoverRequest(int link, DWORD v) {
|
||||
}
|
||||
void TextWindow::ScreenSelectConstraint(int link, DWORD v) {
|
||||
SS.GW.ClearSelection();
|
||||
SS.GW.selection[0].constraint.v = v;
|
||||
GraphicsWindow::Selection sel;
|
||||
ZERO(&sel);
|
||||
sel.constraint.v = v;
|
||||
SS.GW.selection.Add(&sel);
|
||||
}
|
||||
void TextWindow::ScreenSelectRequest(int link, DWORD v) {
|
||||
hRequest hr = { v };
|
||||
SS.GW.ClearSelection();
|
||||
SS.GW.selection[0].entity = hr.entity(0);
|
||||
GraphicsWindow::Selection sel;
|
||||
ZERO(&sel);
|
||||
hRequest hr = { v };
|
||||
sel.entity = hr.entity(0);
|
||||
SS.GW.selection.Add(&sel);
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeGroupOption(int link, DWORD v) {
|
||||
|
@ -522,7 +522,8 @@ void TextWindow::DescribeSelection(void) {
|
||||
} else if(gs.n == 0) {
|
||||
Printf(true, "%FtSELECTED:%E comment text");
|
||||
} else {
|
||||
Printf(true, "%FtSELECTED:%E %d item%s", gs.n, gs.n == 1 ? "" : "s");
|
||||
int n = SS.GW.selection.n;
|
||||
Printf(true, "%FtSELECTED:%E %d item%s", n, n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
if(shown.screen == SCREEN_STYLE_INFO &&
|
||||
|
26
ui.h
26
ui.h
@ -236,7 +236,12 @@ public:
|
||||
// Edit
|
||||
MNU_UNDO,
|
||||
MNU_REDO,
|
||||
MNU_CUT,
|
||||
MNU_COPY,
|
||||
MNU_PASTE,
|
||||
MNU_DELETE,
|
||||
MNU_SELECT_CHAIN,
|
||||
MNU_INVERT_SEL,
|
||||
MNU_SNAP_TO_GRID,
|
||||
MNU_ROTATE_90,
|
||||
MNU_UNSELECT_ALL,
|
||||
@ -321,6 +326,7 @@ public:
|
||||
Vector projRight;
|
||||
Vector projUp;
|
||||
Point2d mouse;
|
||||
Point2d mouseOnButtonDown;
|
||||
bool startedMoving;
|
||||
} orig;
|
||||
|
||||
@ -357,7 +363,7 @@ public:
|
||||
// Operations that must be completed by doing something with the mouse
|
||||
// are noted here. These occupy the same space as the menu ids.
|
||||
static const int FIRST_PENDING = 0x0f000000;
|
||||
static const int DRAGGING_POINT = 0x0f000000;
|
||||
static const int DRAGGING_POINTS = 0x0f000000;
|
||||
static const int DRAGGING_NEW_POINT = 0x0f000001;
|
||||
static const int DRAGGING_NEW_LINE_POINT = 0x0f000002;
|
||||
static const int DRAGGING_NEW_CUBIC_POINT = 0x0f000003;
|
||||
@ -370,6 +376,7 @@ public:
|
||||
int operation;
|
||||
|
||||
hEntity point;
|
||||
List<hEntity> points;
|
||||
hEntity circle;
|
||||
hEntity normal;
|
||||
hConstraint constraint;
|
||||
@ -399,9 +406,10 @@ public:
|
||||
// The current selection.
|
||||
class Selection {
|
||||
public:
|
||||
int tag;
|
||||
|
||||
hEntity entity;
|
||||
hConstraint constraint;
|
||||
|
||||
bool emphasized;
|
||||
|
||||
void Draw(void);
|
||||
@ -410,13 +418,14 @@ public:
|
||||
bool IsEmpty(void);
|
||||
bool Equals(Selection *b);
|
||||
bool IsStylable(void);
|
||||
bool HasEndpoints(void);
|
||||
};
|
||||
Selection hover;
|
||||
static const int MAX_SELECTED = 32;
|
||||
Selection selection[MAX_SELECTED];
|
||||
List<Selection> selection;
|
||||
void HitTestMakeSelection(Point2d mp);
|
||||
void ClearSelection(void);
|
||||
void ClearNonexistentSelectionItems(void);
|
||||
static const int MAX_SELECTED = 32;
|
||||
struct {
|
||||
hEntity point[MAX_SELECTED];
|
||||
hEntity entity[MAX_SELECTED];
|
||||
@ -437,13 +446,14 @@ public:
|
||||
int constraints;
|
||||
int stylables;
|
||||
int comments;
|
||||
int withEndpoints;
|
||||
int n;
|
||||
} gs;
|
||||
void GroupSelection(void);
|
||||
void ToggleSelectionStateOfHovered(void);
|
||||
void ToggleSelectionStateOf(hEntity he);
|
||||
void ToggleSelectionStateOf(Selection *s);
|
||||
void ClearSuper(void);
|
||||
|
||||
static const int CMNU_TOGGLE_SELECTION = 0x100;
|
||||
static const int CMNU_UNSELECT_ALL = 0x101;
|
||||
static const int CMNU_DELETE_SEL = 0x102;
|
||||
static const int CMNU_NEW_CUSTOM_STYLE = 0x103;
|
||||
@ -453,6 +463,7 @@ public:
|
||||
static const int CMNU_OTHER_ANGLE = 0x107;
|
||||
static const int CMNU_STYLE_INFO = 0x108;
|
||||
static const int CMNU_SNAP_TO_GRID = 0x109;
|
||||
static const int CMNU_SELECT_CHAIN = 0x10a;
|
||||
static const int CMNU_FIRST_STYLE = 0x40000000;
|
||||
void ContextMenuListStyles(void);
|
||||
|
||||
@ -481,6 +492,9 @@ public:
|
||||
|
||||
bool showSnapGrid;
|
||||
|
||||
void AddPointToDraggedList(hEntity hp);
|
||||
void StartDraggingByEntity(hEntity he);
|
||||
void StartDraggingBySelection(void);
|
||||
void UpdateDraggedNum(Vector *pos, double mx, double my);
|
||||
void UpdateDraggedPoint(hEntity hp, double mx, double my);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
multi-drag
|
||||
select loop, all in group, others
|
||||
marquee selection
|
||||
copy and paste
|
||||
background image
|
||||
associative entities from solid model, as a special group
|
||||
|
Loading…
Reference in New Issue
Block a user