/* * Copyright (c) 2008 Hypertriton, Inc. * All rights reserved. * * 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. */ /* * Perform various geometric operations on elements of the sketch. Most of * these routines use functions provided by the math library. */ #include #include #include "sk.h" #include "sk_gui.h" static void ConnectPointsWithLines(SK *sk, int n, SK_Node **nodes) { SK_Line *Ln; int i; if (n < 2) { AG_TextMsg(AG_MSG_ERROR, "Select at least two Points"); return; } for (i = 1; i < n; i++) { Ln = SK_LineNew(sk->root); Ln->p1 = (SK_Point *)nodes[i-1]; Ln->p2 = (SK_Point *)nodes[i]; } } static void TranslatePointsPerVector(SK *sk, int n, SK_Node **nodes) { M_Vector3 v; int i; for (i = 0; i < n; i++) { if (SK_NodeOfClass(nodes[i], "Line:*")) { SK_Line *L = SKLINE(nodes[i]); v = M_VecSub3(SK_Pos(L->p1), SK_Pos(L->p2)); break; } } if (i == n) { AG_TextMsg(AG_MSG_ERROR, "Select at least one Line"); return; } for (i = 0; i < n; i++) { if (!SK_NodeOfClass(nodes[i], "Point:*")) { continue; } SK_Translatev(nodes[i], &v); } } static void SortPoints2(SK *sk, int n, SK_Node **nodes, enum m_point_set_sort_mode2 mode) { M_PointSet2 S = M_POINT_SET_EMPTY; SK_Node *node; SK_Point *pt; int i; for (i = 0; i < n; i++) { if (M_PointSetAdd2(&S, SK_Pos2(nodes[i])) == -1) goto out; } M_PointSetSort2(&S, mode); node = SK_NodeNew(sk->root); SK_NodeSetName(node, _("Sorted Points")); for (i = 0; i < n; i++) { pt = SK_PointNew(node); SK_Translatev(pt, &S.p[i]); } out: M_PointSetFree2(&S); } static void SortPointsByX(SK *sk, int n, SK_Node **nodes) { SortPoints2(sk, n, nodes, M_POINT_SET_SORT_XY); } static void SortPointsByY(SK *sk, int n, SK_Node **nodes) { SortPoints2(sk, n, nodes, M_POINT_SET_SORT_YX); } static void CreateParallelLines(SK *sk, int n, SK_Node **nodes) { int i; for (i = 0; i < n; i++) { M_Line2 L = SK_LineValue(SKLINE(nodes[i])); SK_Line *Ln; M_Vector2 v1, v2; M_LineToPts2(M_LineParallel2(L, +1.0), &v1, &v2); Ln = SK_LineNew(sk->root); Ln->p1 = SK_PointNew(sk->root); Ln->p2 = SK_PointNew(sk->root); SK_TranslateVec(Ln->p1, v1); SK_TranslateVec(Ln->p2, v2); M_LineToPts2(M_LineParallel2(L, -1.0), &v1, &v2); Ln = SK_LineNew(sk->root); Ln->p1 = SK_PointNew(sk->root); Ln->p2 = SK_PointNew(sk->root); SK_TranslateVec(Ln->p1, v1); SK_TranslateVec(Ln->p2, v2); } } static void DividePointsByLine(SK *sk, int n, SK_Node **nodes) { SK_Node *nLeft = NULL, *nRight = NULL, *nOn = NULL; /* compiler happy */ M_Line2 L; M_Real rv; int i; for (i = 0; i < n; i++) { if (SK_NodeOfClass(nodes[i], "Line:*")) { SK_Line *Ln = SKLINE(nodes[i]); L = SK_LineValue(SKLINE(nodes[i])); nLeft = SK_NodeNew(sk->root); nRight = SK_NodeNew(sk->root); nOn = SK_NodeNew(sk->root); SK_NodeSetName(nLeft, "Left of %s", SKNODE(Ln)->name); SK_NodeSetName(nRight, "Right of %s", SKNODE(Ln)->name); SK_NodeSetName(nOn, "On %s", SKNODE(Ln)->name); break; } } if (i == n) { AG_TextMsg(AG_MSG_ERROR, "Select at least one Line"); return; } for (i = 0; i < n; i++) { if (!SK_NodeOfClass(nodes[i], "Point:*")) { continue; } rv = M_LinePointSide2(L, SK_Pos2(nodes[i])); if (M_MACHZERO(rv)) { SK_NodeMoveToParent(nodes[i], nOn); } else if (rv < -M_MACHEP) { SK_NodeMoveToParent(nodes[i], nLeft); } else if (rv > +M_MACHEP) { SK_NodeMoveToParent(nodes[i], nRight); } } } static void ComputeLinePointDistance(SK *sk, int n, SK_Node **nodes) { SK_Line *Ln = NULL; SK_Point *Pn = NULL; M_Real dist; int i; for (i = 0; i < n; i++) { if (SK_NodeOfClass(nodes[i], "Point:*")) { Pn = SKPOINT(nodes[i]); } else if (SK_NodeOfClass(nodes[i], "Line:*")) { Ln = SKLINE(nodes[i]); } } if (Pn == NULL || Ln == NULL) { AG_TextMsg(AG_MSG_ERROR, _("Select a Line and a Point")); return; } dist = M_LinePointDistance2(SK_LineValue(Ln), SK_Pos2(Pn)); AG_TextMsg(AG_MSG_INFO, "%s-%s distance = %.04f", SKNODE(Ln)->name, SKNODE(Pn)->name, dist); SK_SetStatus(sk, sk->status, "||%s - %s|| = %.04f", SKNODE(Ln)->name, SKNODE(Pn)->name, dist); } static void ComputeLineLineAngle(SK *sk, int n, SK_Node **nodes) { SK_Line *Ln[2]; int i, nLines = 0; M_Real theta; for (i = 0; i < n; i++) { if (SK_NodeOfClass(nodes[i], "Line:*") && nLines < 2) { Ln[nLines++] = SKLINE(nodes[i]); } } if (nLines != 2) { AG_TextMsg(AG_MSG_ERROR, _("Select exactly two Lines")); return; } theta = Degrees(M_LineLineAngle2(SK_LineValue(Ln[0]), SK_LineValue(Ln[1]))); AG_TextMsg(AG_MSG_INFO, "%s-%s angle = %.04f\xc2\xb0", SKNODE(Ln[0])->name, SKNODE(Ln[1])->name, theta); SK_SetStatus(sk, sk->status, "Acos(%s \xc2\xb7 %s) = %.04f\xc2\xb0", SKNODE(Ln[0])->name, SKNODE(Ln[1])->name, theta); } static void TestPolygonConvexity(SK *sk, int n, SK_Node **nodes) { int i; for (i = 0; i < n; i++) { M_Polygon P = SK_PolygonValue(SKPOLYGON(nodes[i])); int rv; rv = M_PolygonIsConvex(&P); if (rv == 1) { AG_TextMsg(AG_MSG_INFO, _("%s is convex"), nodes[i]->name); } else if (rv == 0) { AG_TextMsg(AG_MSG_INFO, _("%s is concave"), nodes[i]->name); } else { AG_TextMsg(AG_MSG_INFO, _("%s is invalid"), nodes[i]->name); } } } static void GeometryOp(AG_Event *event) { SK_View *skv = AG_PTR(1); const SK_GeometryFn *fn = AG_PTR(2); SK_Node **nodes, *node; int nNodes = 0; char *mask, *s; mask = Strdup(fn->mask); nodes = Malloc(sizeof(SK_Node **)); while ((s = AG_Strsep(&mask, ",")) != NULL) { SK_FOREACH_NODE(node, skv->sk, sk_node) { if (SKNODE_SELECTED(node) && SK_NodeOfClass(node, s)) { nodes = Realloc(nodes, (nNodes+1) * sizeof(SK_Node **)); nodes[nNodes++] = node; } } } if (nNodes > 0) { fn->fn(skv->sk, nNodes, nodes); } else { AG_TextMsg(AG_MSG_ERROR, "Must select: %s", fn->mask); } Free(mask); Free(s); } void SK_GeometryMenu(SK_View *skv, void *pMenu) { AG_MenuItem *m = pMenu; Uint i; for (i = 0; i < skGeometryFnCount; i++) { const SK_GeometryFn *fn = &skGeometryFns[i]; AG_MenuAction(m, fn->name, NULL, GeometryOp, "%p,%p", skv, fn); } } const SK_GeometryFn skGeometryFns[] = { { "Connect Points with Lines", "Point:*", ConnectPointsWithLines }, { "Translate Points per vector", "Point:*,Line:*", TranslatePointsPerVector }, { "Sort Points by X-coordinate", "Point:*", SortPointsByX }, { "Sort Points by Y-coordinate", "Point:*", SortPointsByY }, { "Divide Points with Line", "Point:*,Line:*", DividePointsByLine }, { "Create parallel Lines", "Line:*", CreateParallelLines }, { "Compute Line-Point distance", "Point:*,Line:*", ComputeLinePointDistance }, { "Compute Line-Line angle", "Line:*,Line:*", ComputeLineLineAngle }, { "Test Polygon convexity", "Polygon:*", TestPolygonConvexity }, }; const Uint skGeometryFnCount = sizeof(skGeometryFns)/sizeof(skGeometryFns[0]);