/*
* Copyright (c) 2007-2010 Hypertriton, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Dimension annotation. Creates distance and angle constraints, or
* display effective distances, angles and radii.
*/
#include
#include
#include "sk.h"
#include "sk_gui.h"
#include
const char *skDimensionTypeNames[] = {
"None",
"Distance",
"Angle (at endpoint)",
"Angle (at intersection)",
NULL
};
SK_Dimension *
SK_DimensionNew(void *parent)
{
SK_Dimension *dim;
dim = Malloc(sizeof(SK_Dimension));
SK_DimensionInit(dim, SK_GenNodeName(SKNODE(parent)->sk, "Dimension"));
SK_NodeAttach(parent, dim);
return (dim);
}
void
SK_DimensionInit(void *p, Uint name)
{
SK_Dimension *dim = p;
SK_AnnotInit(dim, name, &skDimensionOps);
dim->unit = NULL;
dim->n1 = NULL;
dim->n2 = NULL;
dim->cLbl = M_ColorRGB(0.0, 0.0, 0.5);
dim->cLblBorder = M_ColorRGB(0.0, 0.0, 0.5);
dim->cLineDim = M_ColorRGB(0.0, 0.0, 0.5);
dim->vLbl = M_VecGet3(0.0, 1.0, 0.0);
dim->wLbl = 1.0;
dim->hLbl = 1.0;
dim->text[0] = '\0';
dim->xPad = 5;
dim->yPad = 2;
dim->type = SK_DIMENSION_NONE;
}
int
SK_DimensionLoad(SK *sk, void *p, AG_DataSource *buf)
{
char unitKey[128];
SK_Dimension *dim = p;
if (SK_AnnotLoad(sk, dim, buf) == -1)
return (-1);
dim->type = (enum sk_dimension_type)AG_ReadUint32(buf);
dim->flags = (Uint)AG_ReadUint32(buf);
AG_CopyString(unitKey, buf, sizeof(unitKey));
if (unitKey[0] == '\0') {
dim->unit = NULL;
} else {
if ((dim->unit = AG_FindUnit(unitKey)) == NULL)
return (-1);
}
dim->cLbl = M_ReadColor(buf);
dim->cLblBorder = M_ReadColor(buf);
dim->cLineDim = M_ReadColor(buf);
dim->xPad = (int)AG_ReadUint8(buf);
dim->yPad = (int)AG_ReadUint8(buf);
dim->vLbl = M_ReadVector3(buf);
dim->n1 = SK_ReadRef(buf, sk, NULL);
dim->n2 = SK_ReadRef(buf, sk, NULL);
if (dim->n1 == NULL || dim->n2 == NULL) {
AG_SetError("Missing dimension nodes (%s)", AG_GetError());
return (-1);
}
dim->text[0] = '\0';
return (0);
}
int
SK_DimensionSave(SK *sk, void *p, AG_DataSource *buf)
{
SK_Dimension *dim = p;
SK_AnnotSave(sk, dim, buf);
AG_WriteUint32(buf, (Uint32)dim->type);
AG_WriteUint32(buf, (Uint32)dim->flags);
AG_WriteString(buf, dim->unit != NULL ? dim->unit->key : "");
M_WriteColor(buf, &dim->cLbl);
M_WriteColor(buf, &dim->cLblBorder);
M_WriteColor(buf, &dim->cLineDim);
AG_WriteUint8(buf, (Uint8)dim->xPad);
AG_WriteUint8(buf, (Uint8)dim->yPad);
M_WriteVector3(buf, &dim->vLbl);
SK_WriteRef(buf, dim->n1);
SK_WriteRef(buf, dim->n2);
return (0);
}
/*
* Compute the minimal distance between L and v, and return the
* closest point on L into pvC.
*/
static M_Real
PointLineDistance(const M_Vector3 *_Nonnull v, const SK_Line *_Nonnull L,
M_Vector3 *_Nullable pvC)
{
M_Vector3 p1 = SK_Pos(L->p1);
M_Vector3 p2 = SK_Pos(L->p2);
M_Vector3 vC;
M_Real mag = M_VecDistance3(p1,p2);
M_Real u;
u = ( ((v->x - p1.x)*(p2.x - p1.x)) +
((v->y - p1.y)*(p2.y - p1.y)) ) / (mag*mag);
vC = M_VecAdd3(p1, M_VecScale3(M_VecSub3p(&p2,&p1), u));
if (pvC != NULL) {
M_VecCopy3(pvC, &vC);
}
return M_VecDistance3p(v,&vC);
}
/*
* For a given dimension, return a transformation matrix to take us from the
* sketch's coordinate system to a coordinate system where the x axis is the
* line between the two dimensioned entities and the origin is this line's
* midpoint.
*
* The sketch coordinates of the two measurement points are returned into
* vr1 and vr2. In the case of point-line distances, not only the vertices
* but any point on the line could be returned.
*/
static M_Matrix44
MapToAnnotation(SK_Dimension *_Nonnull dim, M_Vector3 *_Nullable vr1,
M_Vector3 *_Nullable vr2, M_Vector3 *_Nullable vShared)
{
M_Matrix44 T;
M_Vector3 v1, v2;
M_Vector3 v;
M_Real theta;
M_MatIdentity44v(&T);
switch (dim->type) {
case SK_DIMENSION_DISTANCE:
/*
* Find the positions v1,v2 of the two entities. For point-line
* distances, we use the closest point on the line.
*
* Line-line distances entered by the user are expanded to two
* point-point distances so that case is not considered.
*/
if (SK_NodeOfClass(dim->n1, "Line:*") &&
SK_NodeOfClass(dim->n2, "Point:*")) {
v2 = SK_Pos(dim->n2);
PointLineDistance(&v2, SKLINE(dim->n1), &v1);
} else
if (SK_NodeOfClass(dim->n1, "Point:*") &&
SK_NodeOfClass(dim->n2, "Line:*")) {
v1 = SK_Pos(dim->n1);
PointLineDistance(&v1, SKLINE(dim->n2), &v2);
} else {
v1 = SK_Pos(dim->n1);
v2 = SK_Pos(dim->n2);
}
v = M_VecSub3p(&v1, &v2);
theta = Atan2(v.y, v.x);
M_MatTranslate44v(&T, M_VecLERP3p(&v1, &v2, 0.5));
M_MatRotate44K(&T, theta);
if (vr1 != NULL) { M_VecCopy3(vr1, &v1); }
if (vr2 != NULL) { M_VecCopy3(vr2, &v2); }
break;
case SK_DIMENSION_ANGLE_ENDPOINT:
if (SK_LineSharedEndpoint(SKLINE(dim->n1), SKLINE(dim->n2),
&v, &v1, &v2) == -1) {
AG_FatalError("Bogus ANGLE_ENDPOINT");
}
M_MatTranslate44v(&T, v);
M_MatRotate44K(&T, M_PI);
if (vr1 != NULL) { M_VecCopy3(vr1, &v1); }
if (vr2 != NULL) { M_VecCopy3(vr2, &v2); }
if (vShared != NULL) { M_VecCopy3(vShared, &v); }
break;
case SK_DIMENSION_ANGLE_INTERSECT:
break;
default:
break;
}
return (T);
}
static M_Real
GetDimensionVal(SK_Dimension *_Nonnull dim)
{
SK_Node *n1 = dim->n1;
SK_Node *n2 = dim->n2;
M_Vector3 v1, v2;
M_Real rv;
if (SK_NodeOfClass(n1, "Point:*") &&
SK_NodeOfClass(n2, "Point:*")) {
v1 = SK_Pos(n1);
v2 = SK_Pos(n2);
return M_VecDistance3p(&v1,&v2);
} else if (SK_NodeOfClass(n1, "Point:*") &&
SK_NodeOfClass(n2, "Line:*")) {
v1 = SK_Pos(n1);
return PointLineDistance(&v1, SKLINE(n2), NULL);
} else if (SK_NodeOfClass(n1, "Line:*") &&
SK_NodeOfClass(n2, "Point:*")) {
v2 = SK_Pos(n2);
return PointLineDistance(&v2, SKLINE(n1), NULL);
} else if (SK_NodeOfClass(n1, "Line:*") &&
SK_NodeOfClass(n2, "Line:*")) {
rv = SK_LineLineAngleCCW(SKLINE(n1), SKLINE(n2));
if (dim->flags & SK_DIMENSION_CONJ_ANGLE) {
return (M_PI*2.0 - rv);
} else {
return (rv);
}
}
return (M_INFINITY);
}
static void
GetLabelText(SK_Dimension *_Nonnull dim, char *_Nonnull text, size_t text_len)
{
SK *sk = SKNODE(dim)->sk;
switch (dim->type) {
case SK_DIMENSION_DISTANCE:
AG_Snprintf(text, text_len, "%.02f",
AG_Base2Unit(GetDimensionVal(dim), sk->uLen));
break;
case SK_DIMENSION_ANGLE_ENDPOINT:
case SK_DIMENSION_ANGLE_INTERSECT:
AG_Snprintf(text, text_len, "%.02f\xc2\xb0",
Degrees(GetDimensionVal(dim)));
break;
default:
text[0] = '\0';
break;
}
if (SK_ConstrainedNodes(&sk->ctGraph, dim->n1, dim->n2) == NULL)
Strlcat(text, _(" (REF)"), text_len);
}
static __inline__ void
DrawLinearDistance(SK_Dimension *_Nonnull dim, M_Real xPad,
M_Vector3 *_Nonnull v1, M_Vector3 *_Nonnull v2)
{
M_Color cLineDim = SK_NodeColor(dim, &dim->cLineDim);
M_Real y, x1, x2, e1, e2;
M_Real d;
y = dim->vLbl.y + dim->hLbl/2.0;
x1 = dim->vLbl.x - xPad;
x2 = dim->vLbl.x + dim->wLbl + xPad;
d = M_VecDistance3p(v1,v2);
e1 = -d/2.0;
e2 = +d/2.0;
GL_Begin(GL_LINES);
GL_Color3v(&cLineDim);
if ((x1 - e1) > 0.0) {
GL_Vertex2(e1, y);
GL_Vertex2(x1, y);
}
if ((e2 - x2) > 0.0) {
GL_Vertex2(x2, y);
GL_Vertex2(e2, y);
}
GL_Vertex2(e1, 0.0);
GL_Vertex2(e1, y);
GL_Vertex2(e2, 0.0);
GL_Vertex2(e2, y);
GL_End();
}
static __inline__ void
DrawAngle(SK_Dimension *_Nonnull dim, M_Vector3 *_Nonnull vShared,
M_Vector3 *_Nonnull v1, M_Vector3 *_Nonnull v2, SK_View *_Nonnull skv)
{
M_Color cLineDim = SK_NodeColor(dim, &dim->cLineDim);
M_Real r = 10.0*skv->wPixel;
M_Real incr = (2.0*M_PI)/30.0;
M_Real a1, a2, i;
if (dim->flags & SK_DIMENSION_CONJ_ANGLE) {
a1 = Atan2(v1->y - vShared->y, v1->x - vShared->x) - M_PI;
a2 = Atan2(v2->y - vShared->y, v2->x - vShared->x) - M_PI;
} else {
a1 = Atan2(v2->y - vShared->y, v2->x - vShared->x) - M_PI;
a2 = Atan2(v1->y - vShared->y, v1->x - vShared->x) - M_PI;
}
while (a1 > a2)
a2 += M_PI*2.0;
GL_Begin(GL_LINE_STRIP);
GL_Color3v(&cLineDim);
GL_Vertex2(Cos(a1)*r, Sin(a1)*r);
for (i = a1+incr; i < a2-incr; i += incr) {
GL_Vertex2(Cos(i)*r, Sin(i)*r);
}
GL_Vertex2(Cos(a2)*r, Sin(a2)*r);
GL_End();
}
static SK_DimensionView *_Nullable
GetNodeData(SK_View *_Nonnull skv, SK_Dimension *_Nonnull dim)
{
SK_DimensionView *dimv;
if ((dimv = SK_ViewGetNodeData(skv, dim)) == NULL) {
dimv = Malloc(sizeof(SK_DimensionView));
dimv->lbl = -1;
printf("set node data %p -> %p\n", dim, dimv);
SK_ViewSetNodeData(skv, dim, dimv);
}
return (dimv);
}
void
SK_DimensionDraw(void *p, SK_View *skv)
{
char text[SK_DIMENSION_TEXT_MAX];
SK_Dimension *dim = p;
M_Color cLblBorder = SK_NodeColor(dim, &dim->cLblBorder);
M_Vector3 v1, v2, vShared;
M_Matrix44 Ta;
int wText, hText;
M_Real xPad = ((M_Real)dim->xPad)*skv->wPixel;
M_Real yPad = ((M_Real)dim->yPad)*skv->hPixel;
SK_DimensionView *dimv;
if (dim->n1 == NULL || dim->n2 == NULL)
return;
dimv = GetNodeData(skv, dim);
GetLabelText(dim, text, sizeof(text));
AG_TextSize(text, &wText, &hText);
dim->wLbl = (M_Real)wText * skv->wPixel;
dim->hLbl = (M_Real)hText * skv->hPixel;
if (strcmp(text, dim->text) != 0) {
Strlcpy(dim->text, text, sizeof(dim->text));
if (dimv->lbl != -1) {
AG_WidgetUnmapSurface(skv, dimv->lbl);
dimv->lbl = -1;
}
}
if (dimv->lbl == -1)
SK_DimensionRedraw(dim, skv);
GL_PushMatrix();
Ta = MapToAnnotation(dim, &v1, &v2, &vShared);
M_MatTranspose44v(&Ta);
GL_MultMatrixv(&Ta);
switch (dim->type) {
case SK_DIMENSION_DISTANCE:
DrawLinearDistance(dim, xPad, &v1, &v2);
break;
case SK_DIMENSION_ANGLE_ENDPOINT:
DrawAngle(dim, &vShared, &v1, &v2, skv);
break;
default:
break;
}
GL_Translate(dim->vLbl);
AG_WidgetBlitSurfaceFlippedGL(skv, dimv->lbl,
(float)dim->wLbl, (float)dim->hLbl);
GL_Begin(GL_LINE_LOOP);
{
GL_Color3v(&cLblBorder);
GL_Vertex2(-xPad, -yPad);
GL_Vertex2(dim->wLbl+xPad, -yPad);
GL_Vertex2(dim->wLbl+xPad, dim->hLbl+yPad);
GL_Vertex2(-xPad, dim->hLbl+yPad);
}
GL_End();
GL_PopMatrix();
}
void
SK_DimensionRedraw(void *p, SK_View *skv)
{
SK_Dimension *dim = p;
SK_DimensionView *dimv = GetNodeData(skv, dim);
M_Color cLbl = SK_NodeColor(dim, &dim->cLbl);
AG_PushTextState();
AG_TextColorRGB((Uint8)(cLbl.r*255.0),
(Uint8)(cLbl.g*255.0),
(Uint8)(cLbl.b*255.0));
if (dimv->lbl == -1) {
dimv->lbl = AG_WidgetMapSurface(skv,
AG_TextRender(dim->text));
} else {
AG_WidgetReplaceSurface(skv, dimv->lbl,
AG_TextRender(dim->text));
}
AG_PopTextState();
}
void
SK_DimensionEdit(void *p, AG_Widget *box, SK_View *skv)
{
SK_Dimension *dim = p;
AG_MSpinbutton *msb;
AG_MFSpinbutton *mfsb;
AG_LabelNew(box, 0, "Type: %s", skDimensionTypeNames[dim->type]);
AG_LabelNew(box, 0, "Units: %s", dim->unit != NULL ? dim->unit->abbr :
"(default)");
msb = AG_MSpinbuttonNew(box, 0, ",", "Padding: ");
AG_BindInt(msb, "xvalue", &dim->xPad);
AG_BindInt(msb, "yvalue", &dim->yPad);
mfsb = AG_MFSpinbuttonNew(box, 0, NULL, ",", "Position: ");
#if defined(SINGLE_PRECISION) || defined(HAVE_SSE)
AG_BindFloat(mfsb, "xvalue", &dim->vLbl.x);
AG_BindFloat(mfsb, "yvalue", &dim->vLbl.y);
#else
M_BindReal(mfsb, "xvalue", &dim->vLbl.x);
M_BindReal(mfsb, "yvalue", &dim->vLbl.y);
#endif
}
M_Real
SK_DimensionProximity(void *p, const M_Vector3 *v, M_Vector3 *vC)
{
SK_Dimension *dim = p;
M_Vector3 p1, p2;
M_Real r, u;
M_Matrix44 M;
if (dim->n1 == NULL || dim->n2 == NULL)
return (M_INFINITY);
M = MapToAnnotation(dim, NULL, NULL, NULL);
p1 = dim->vLbl;
p1.y += dim->hLbl/2.0;
p1 = M_VecFromProj3(M_MatMultVector44(M, M_VecToProj3(p1,1)));
p2 = dim->vLbl;
p2.x += dim->wLbl;
p2.y += dim->hLbl/2.0;
p2 = M_VecFromProj3(M_MatMultVector44(M, M_VecToProj3(p2,1)));
r = M_VecDistance3(p2,p1);
u = ( ((v->x - p1.x)*(p2.x - p1.x)) +
((v->y - p1.y)*(p2.y - p1.y)) ) / (r*r);
if (u < 0.0) {
*vC = p1;
} else if (u > 1.0) {
*vC = p2;
} else {
*vC = M_VecAdd3(p1, M_VecScale3(M_VecSub3(p2,p1), u));
}
return M_VecDistance3p(v,vC);
}
int
SK_DimensionDelete(void *p)
{
SK_Dimension *dim = p;
return SK_AnnotDelete(dim);
}
int
SK_DimensionMove(void *p, const M_Vector3 *pos, const M_Vector3 *vel)
{
SK_Dimension *dim = p;
M_Vector3 v;
M_Matrix44 Ta, TaInv;
Ta = MapToAnnotation(dim, NULL, NULL, NULL);
v = M_VecFromProj3(M_MatMultVector44(Ta, M_VecToProj3(dim->vLbl,1)));
M_VecAdd3v(&v, vel);
TaInv = M_MatInvert44(Ta);
dim->vLbl = M_VecFromProj3(M_MatMultVector44(TaInv, M_VecToProj3(v,1)));
return (0);
}
void
SK_DimensionSetUnit(SK_Dimension *dim, const AG_Unit *unit)
{
dim->unit = unit;
}
SK_NodeOps skDimensionOps = {
"Annot:Dimension",
sizeof(SK_Dimension),
0,
SK_DimensionInit,
NULL, /* destroy */
SK_DimensionLoad,
SK_DimensionSave,
SK_DimensionDraw,
SK_DimensionRedraw,
SK_DimensionEdit,
SK_DimensionProximity,
SK_DimensionDelete,
SK_DimensionMove,
NULL, /* constrained */
};
#ifdef EDITION
struct sk_dimension_tool {
SK_Tool tool;
SK_Dimension *curDim;
};
static void
AddDimConstraint(AG_Event *_Nonnull event)
{
AG_Window *pWin = AG_PTR(1);
struct sk_dimension_tool *t = AG_PTR(2);
SK_Dimension *dim = AG_PTR(3);
AG_Numerical *num = AG_PTR(4);
SK *sk = SKTOOL(t)->skv->sk;
switch (dim->type) {
case SK_DIMENSION_DISTANCE:
SK_AddConstraint(&sk->ctGraph, dim->n1, dim->n2,
SK_DISTANCE, M_NumericalGetReal(num));
SK_Update(sk);
break;
case SK_DIMENSION_ANGLE_ENDPOINT:
case SK_DIMENSION_ANGLE_INTERSECT:
SK_AddConstraint(&sk->ctGraph, dim->n1, dim->n2,
SK_ANGLE, M_NumericalGetReal(num));
SK_Update(sk);
break;
default:
break;
}
t->curDim = NULL;
AG_ObjectDetach(pWin);
}
static void
UndoDimConstraint(AG_Event *_Nonnull event)
{
AG_Window *pWin = AG_PTR(1);
struct sk_dimension_tool *t = AG_PTR(2);
SK_DimensionDelete(t->curDim);
t->curDim = NULL;
AG_ObjectDetach(pWin);
}
static int
AddDimConstraintDlg(struct sk_dimension_tool *_Nonnull t, SK *_Nonnull sk,
SK_Dimension *_Nonnull dim)
{
AG_Window *win;
AG_Numerical *num;
AG_VBox *vb;
AG_HBox *hb;
const char *unit = NULL;
#if 0
if (SK_FindConstraint(&sk->ctGraph, SK_CONSTRAINT_ANY, dim->n1, dim->n2)
!= NULL) {
AG_TextMsg(AG_MSG_ERROR,
_("Existing constraint; new %s constraint would "
"overconstraint sketch."),
skConstraintNames[SK_DISTANCE]);
return (-1);
}
#endif
win = AG_WindowNew(AG_WINDOW_MODAL|AG_WINDOW_NOVRESIZE|
AG_WINDOW_NOCLOSE);
AG_WindowSetPosition(win, AG_WINDOW_CENTER, 1);
vb = AG_VBoxNew(win, AG_VBOX_HFILL);
{
switch (dim->type) {
case SK_DIMENSION_DISTANCE:
AG_WindowSetCaptionS(win, _("Distance constraint"));
AG_LabelNew(vb, 0,
_("Distance between %s and %s:"),
dim->n1->name, dim->n2->name);
unit = "mm";
break;
case SK_DIMENSION_ANGLE_ENDPOINT:
case SK_DIMENSION_ANGLE_INTERSECT:
AG_WindowSetCaptionS(win, _("Angle constraint"));
AG_LabelNew(vb, 0,
_("Angle between %s and %s:"),
dim->n1->name, dim->n2->name);
unit = "deg";
break;
default:
AG_SetError("Bad dimension type");
return (-1);
}
}
vb = AG_VBoxNew(win, AG_VBOX_HFILL);
{
static double v;
/* XXX */
num = AG_NumericalNewS(vb, AG_NUMERICAL_HFILL, unit, NULL);
AG_WidgetFocus(num);
v = GetDimensionVal(dim);
AG_BindDouble(num, "value", &v);
AG_SetDouble(num, "inc", AG_Unit2Base(1.0, AG_FindUnit(unit)));
AG_SetEvent(num, "numerical-return",
AddDimConstraint, "%p,%p,%p,%p", win, t, dim, num);
}
hb = AG_HBoxNew(win, AG_HBOX_HFILL|AG_HBOX_HOMOGENOUS);
{
AG_ButtonNewFn(hb, 0, _("OK"),
AddDimConstraint, "%p,%p,%p,%p", win, t, dim, num);
AG_ButtonNewFn(hb, 0, _("Cancel"),
UndoDimConstraint, "%p,%p", win, t);
}
AG_WindowShow(win);
return (0);
}
static void
ToolInit(void *_Nonnull p)
{
struct sk_dimension_tool *t = p;
t->curDim = NULL;
}
static int
ToolMouseMotion(void *_Nonnull self, M_Vector3 pos, M_Vector3 vel, int btn)
{
struct sk_dimension_tool *t = self;
SK_View *skv = SKTOOL(t)->skv;
SK *sk = skv->sk;
M_Vector3 vC;
SK_Node *node;
TAILQ_FOREACH(node, &sk->nodes, nodes) {
node->flags &= ~(SK_NODE_MOUSEOVER);
SK_NodeRedraw(node, skv);
}
if ((node = SK_ProximitySearch(sk, "Point", &pos, &vC, NULL)) != NULL &&
M_VecDistance3p(&pos,&vC) < skv->rSnap) {
node->flags |= SK_NODE_MOUSEOVER;
SK_NodeRedraw(node, skv);
} else {
if ((node = SK_ProximitySearch(sk, NULL, &pos, &vC, NULL))
!= NULL) {
node->flags |= SK_NODE_MOUSEOVER;
SK_NodeRedraw(node, skv);
}
}
if (t->curDim != NULL &&
t->curDim->n1 != NULL &&
t->curDim->n2 != NULL) {
M_Matrix44 Ta, TaInv;
Ta = MapToAnnotation(t->curDim, NULL, NULL, NULL);
TaInv = M_MatInvert44(Ta);
t->curDim->vLbl = M_VecFromProj3(M_MatMultVector44(TaInv,
M_VecToProj3(pos,1)));
}
return (0);
}
static int
ToolMouseButtonDown(void *_Nonnull self, M_Vector3 pos, int btn)
{
struct sk_dimension_tool *t = self;
SK_View *skv = SKTOOL(t)->skv;
SK *sk = skv->sk;
SK_Node *n;
M_Vector3 vC;
if (btn == AG_MOUSE_RIGHT) {
if (t->curDim != NULL) {
goto undo;
}
return (0);
} else if (btn != AG_MOUSE_LEFT) {
return (0);
}
if ((n = SK_ProximitySearch(sk, "Point", &pos, &vC, NULL)) == NULL ||
M_VecDistance3p(&pos,&vC) >= skv->rSnap) {
if ((n = SK_ProximitySearch(sk, "Line", &pos, &vC, NULL))
== NULL)
n = SK_ProximitySearch(sk, NULL, &pos, &vC, NULL);
}
if (t->curDim == NULL) {
if (n != NULL) {
t->curDim = SK_DimensionNew(sk->root);
t->curDim->n1 = n;
} else {
goto undo;
}
} else if (t->curDim->n2 == NULL) {
if (n != NULL) {
t->curDim->n2 = n;
if (SK_NodeOfClass(t->curDim->n1, "Line:*") &&
SK_NodeOfClass(t->curDim->n2, "Line:*")) {
SK_Line *L1 = SKLINE(t->curDim->n1);
SK_Line *L2 = SKLINE(t->curDim->n2);
if (L1->p1 == L2->p1 || L1->p1 == L2->p2 ||
L1->p2 == L2->p1 || L1->p2 == L2->p2) {
t->curDim->type =
SK_DIMENSION_ANGLE_ENDPOINT;
if (SK_LineLineAngleCCW(L1, L2) > M_PI)
{
t->curDim->flags |=
SK_DIMENSION_CONJ_ANGLE;
}
} else {
t->curDim->type =
SK_DIMENSION_ANGLE_INTERSECT;
}
} else {
M_Vector3 v1, v2;
SK_Node *nTmp;
t->curDim->type = SK_DIMENSION_DISTANCE;
/*
* The order of the points determine the
* handedness of the annotation's coordinate
* system, so we arrange for the text to
* display left to right initially.
*/
v1 = SK_Pos(t->curDim->n1);
v2 = SK_Pos(t->curDim->n2);
if (v1.x > v2.x) {
nTmp = t->curDim->n1;
t->curDim->n1 = t->curDim->n2;
t->curDim->n2 = nTmp;
}
}
} else {
goto undo;
}
} else {
if (AddDimConstraintDlg(t, sk, t->curDim) == -1)
goto undo;
}
return (0);
undo:
SK_DimensionDelete(t->curDim);
t->curDim = NULL;
return (0);
}
SK_ToolOps skDimensionToolOps = {
N_("Dimension"),
N_("Specify distances, angles and radii"),
NULL,
sizeof(struct sk_dimension_tool),
0,
ToolInit,
NULL, /* destroy */
NULL, /* edit */
ToolMouseMotion,
ToolMouseButtonDown,
NULL, /* buttonup */
NULL, /* keydown */
NULL /* keyup */
};
#endif /* EDITION */