static void _path_transform (StdPath *sp, const DiaMatrix *m) { BezPoint *bezier = sp->points; gsize n = sp->num_points; guint i; for (i = 0; i < n; ++i) transform_bezpoint (&bezier[i], m); stdpath_update_data (sp); }
/*! * \brief Change the object state regarding selection * \memberof _StdPath */ static gboolean stdpath_transform(StdPath *stdpath, const DiaMatrix *m) { int i; g_return_val_if_fail (m != NULL, FALSE); for (i = 0; i < stdpath->num_points; i++) transform_bezpoint (&stdpath->points[i], m); stdpath_update_data(stdpath); return TRUE; }
/*! * \brief Move the whole object to the given position * * If the object position does not change the whole object should not either. * This is used as a kludge to call the protected update_data() function * * \memberof StdPath */ static ObjectChange* stdpath_move (StdPath *stdpath, Point *to) { DiaObject *obj = &stdpath->object; Point delta = *to; int i; point_sub (&delta, &obj->position); for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; point_add (&bp->p1, &delta); point_add (&bp->p2, &delta); point_add (&bp->p3, &delta); } stdpath_update_data (stdpath); return NULL; }
/*! * \brief Set the object state from the given proeprty vector * \memberof StdPath */ static void stdpath_set_props (StdPath *stdpath, GPtrArray *props) { Property *prop; stdpath->show_background = (stdpath->stroke_or_fill & PDO_FILL) != 0; object_set_props_from_offsets(&stdpath->object, stdpath_offsets, props); /* Usually the list wont contain "show_background", but if it * it set let it take precedence */ if ( (prop = find_prop_by_name (props, "show_background")) != NULL && (prop->experience & PXP_NOTSET) == 0) { if (stdpath->show_background) stdpath->stroke_or_fill |= PDO_FILL; else stdpath->stroke_or_fill &= ~PDO_FILL; } /* now when transfering properties from text we'll loose stroke and fill * Instead of drawing nothing maket it just fill. */ if (!stdpath->stroke_or_fill) stdpath->stroke_or_fill = PDO_FILL; stdpath_update_data (stdpath); }
/*! * \brief Move one of the objects handles * \memberof StdPath */ static ObjectChange* stdpath_move_handle (StdPath *stdpath, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers) { DiaObject *obj = &stdpath->object; Point p0 = obj->position; if (stdpath->move_reason != reason) { if (HANDLE_MOVE_USER == reason) g_print ("move-user"); else if (HANDLE_MOVE_USER_FINAL == reason) g_print ("move-user-final\n"); else if (HANDLE_MOVE_CONNECTED == reason) g_print ("move-connected\n"); else g_print ("move-?reason?\n"); stdpath->move_reason = reason; } else { g_print ("*"); } if (handle->id == HANDLE_RESIZE_SE) { /* scale both directions - keep aspect ratio? */ Point p1 = stdpath->handles[7].pos; real sx, sy; g_assert(stdpath->handles[7].id == handle->id); sx = (to->x - p0.x) / (p1.x - p0.x); sy = (to->y - p0.y) / (p1.y - p0.y); _stdpath_scale (stdpath, sx, sy); stdpath_update_data (stdpath); } else if (handle->id == HANDLE_RESIZE_S) { /* scale height */ Point p1 = stdpath->handles[6].pos; real sy; int i; g_assert(stdpath->handles[6].id == handle->id); sy = (to->y - p0.y) / (p1.y - p0.y); for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; bp->p1.y = p0.y + (bp->p1.y - p0.y) * sy; bp->p2.y = p0.y + (bp->p2.y - p0.y) * sy; bp->p3.y = p0.y + (bp->p3.y - p0.y) * sy; } stdpath_update_data (stdpath); } else if (handle->id == HANDLE_RESIZE_E) { /* scale width */ Point p1 = stdpath->handles[4].pos; real sx; int i; g_assert(stdpath->handles[4].id == handle->id); sx = (to->x - p0.x) / (p1.x - p0.x); for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; bp->p1.x = p0.x + (bp->p1.x - p0.x) * sx; bp->p2.x = p0.x + (bp->p2.x - p0.x) * sx; bp->p3.x = p0.x + (bp->p3.x - p0.x) * sx; } stdpath_update_data (stdpath); } else if (handle->id == _HANDLE_SHEAR) { /* apply horizontal shear weighted by the vertical distance */ Point p1 = stdpath->handles[2].pos; Point pr = stdpath->handles[5].pos; real dx; /* delta x */ real rd = (pr.y - p0.y); /* reference distance */ int i; gboolean revert = FALSE; g_assert(stdpath->handles[2].id == handle->id); g_assert(stdpath->handles[3].id == _HANDLE_REF_POS); g_return_val_if_fail(rd > 0, NULL); dx = to->x - p1.x; /* move_handle needs to be reversible, so do not move more than * the sheared bounding box allows */ do { for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; real sw; /* scaling weight [0..1] to not move the left boundary */ sw = (pr.y - bp->p1.y) / rd; bp->p1.x += dx * sw; sw = (pr.y - bp->p2.y) / rd; bp->p2.x += dx * sw; sw = (pr.y - bp->p3.y) / rd; bp->p3.x += dx * sw; } stdpath_update_data (stdpath); if (!revert && (fabs(stdpath->handles[2].pos.x - (p1.x + dx)) > 0.05)) { dx = -dx; revert = TRUE; } else { revert = FALSE; } } while (revert); } else if (handle->id == _HANDLE_ROTATE) { Point p1 = stdpath->handles[1].pos; Point pr = stdpath->handles[3].pos; Point pc = {stdpath->handles[1].pos.x, stdpath->handles[3].pos.y }; real dx = to->x - p1.x; real dy = pr.y - p1.y; DiaMatrix m; int i; g_assert(stdpath->handles[1].id == handle->id); if (fabs(dx) < 0.001) return NULL; dia_matrix_set_angle_and_scales (&m, -atan2 (dy, dx), 1.0, 1.0); for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; int j; for (j = 0; j < 3; ++j) { Point *p = j == 0 ? &bp->p1 : (j == 1 ? &bp->p2 : &bp->p3); real w, h; w = p->x - pc.x; h = p->y - pc.y; p->x = pc.x + w * m.xx + h * m.xy; p->y = pc.y + w * m.yx + h * m.yy; } } stdpath_update_data (stdpath); } else if (handle->id == _HANDLE_PERSPECTIVE) { Point p1 = stdpath->handles[5].pos; Point pr = stdpath->handles[3].pos; Point cp = { stdpath->handles[1].pos.x, pr.y }; real h2 = p1.y - pr.y; /* relative movement against center and original pos */ real td = (cp.x - to->x) / (cp.x - p1.x) - 1.0; int i; g_assert(stdpath->handles[5].id == handle->id); for (i = 0; i < stdpath->num_points; ++i) { BezPoint *bp = &stdpath->points[i]; Point *p; for (p = &bp->p1; p <= &bp->p3; ++p) { /* the vertical distance from the center point decides about the strength */ real vd = (p->y - cp.y) / h2; /* normalized */ /* the horizontal distance from center gets modified */ real hd = (p->x - cp.x); p->x += (hd * vd * td); } } stdpath_update_data (stdpath); } else if (handle->type != HANDLE_NON_MOVABLE) { g_warning ("stdpath_move_handle() %d not moving", handle->id); } return NULL; }
/*! Factory function - create default object */ static DiaObject * stdpath_create (Point *startpoint, void *user_data, Handle **handle1, Handle **handle2) { StdPath *stdpath; DiaObject *obj; Point sp = {0, 0}; stdpath = g_new0 (StdPath,1); obj = &stdpath->object; obj->type = &stdpath_type; obj->ops = &stdpath_ops; object_init (obj, NUM_HANDLES, 0); stdpath_init_handles (stdpath); if (startpoint) sp = *startpoint; if (user_data == NULL) { /* just to have something to play with <bezpoint type="moveto" p1="0,1"/> <bezpoint type="curveto" p1="0,0" p2="2,2" p3="2,1"/> <bezpoint type="curveto" p1="2,0" p2="0,2" p3="0,1"/> */ BezPoint *bp; stdpath->num_points = 3; bp = stdpath->points = g_new (BezPoint, 3); bp[0].type = BEZ_MOVE_TO; bp[0].p1.x = sp.x + 0; bp[0].p1.y = sp.y + 1; bp[0].p3 = bp[0].p2 = bp[0].p1; /* not strictly necessary */ bp[1].type = BEZ_CURVE_TO; bp[1].p1 = sp; bp[1].p2.x = sp.x + 2; bp[1].p2.y = sp.y + 2; bp[1].p3.x = sp.x + 2; bp[1].p3.y = sp.y + 1; bp[2].type = BEZ_CURVE_TO; bp[2].p1.x = sp.x + 2; bp[2].p1.y = sp.y + 0; bp[2].p2.x = sp.x + 0; bp[2].p2.y = sp.y + 2; bp[2].p3.x = sp.x + 0; bp[2].p3.y = sp.y + 1; } else { BezierCreateData *bcd = (BezierCreateData*)user_data; if (bcd->num_points < 2) { g_warning ("'Standard - Path' needs at least two points"); /* this is a stress test - object might not be setup completely */ object_destroy (obj); g_free (stdpath); return NULL; } stdpath->num_points = bcd->num_points; stdpath->points = g_memdup(bcd->points, bcd->num_points * sizeof(BezPoint)); } stdpath->stroke_or_fill = PDO_STROKE; /* default: stroke only */ stdpath->line_width = attributes_get_default_linewidth(); stdpath->line_color = attributes_get_foreground(); stdpath->fill_color = attributes_get_background(); *handle1 = stdpath->object.handles[0]; *handle2 = stdpath->object.handles[7]; stdpath_update_data (stdpath); return obj; }
/*! * \brief Move one of the objects handles * \memberof _StdPath */ static ObjectChange* stdpath_move_handle (StdPath *stdpath, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers) { const real EPSILON = 0.01; /* move_handle is supposed to be just moving that and related handles (e.g. * when N is moved NE and NW will move too). But 'opposite' handles are not * to be moved at all. So our transformation is invariant to the opposite * point of the path, not the object position. * * Another issue is limit checking. The path object must not get too small, * otherwise it can not be restored by reverse move_handle. So implement a * minimum distance between the moving and the opposite handle. */ if (handle->id == HANDLE_RESIZE_NW) { Point p0 = stdpath->handles[7].pos; /* SE */ Point p1 = stdpath->handles[0].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[0].id == handle->id); g_assert(stdpath->handles[7].id == HANDLE_RESIZE_SE); if (to->x + EPSILON > p0.x) sx = 1.0; if (to->y + EPSILON > p0.y) sy = 1.0; _stdpath_scale (stdpath, sx, sy, &p0); } else if (handle->id == HANDLE_RESIZE_N) { Point p0 = stdpath->handles[6].pos; /* S */ Point p1 = stdpath->handles[1].pos; real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[1].id == handle->id); g_assert(stdpath->handles[6].id == HANDLE_RESIZE_S); if (to->y + EPSILON > p0.y) sy = 1.0; _stdpath_scale (stdpath, 1.0, sy, &p0); } else if (handle->id == HANDLE_RESIZE_NE) { Point p0 = stdpath->handles[5].pos; /* SW */ Point p1 = stdpath->handles[2].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[2].id == handle->id); g_assert(stdpath->handles[5].id == HANDLE_RESIZE_SW); if (to->x - EPSILON < p0.x) sx = 1.0; if (to->y + EPSILON > p0.y) sy = 1.0; _stdpath_scale (stdpath, sx, sy, &p0); } else if (handle->id == HANDLE_RESIZE_W) { /* scale width */ Point p0 = stdpath->handles[4].pos; /* E */ Point p1 = stdpath->handles[3].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); g_assert(stdpath->handles[3].id == handle->id); g_assert(stdpath->handles[4].id == HANDLE_RESIZE_E); if (to->x + EPSILON > p0.x) sx = 1.0; _stdpath_scale (stdpath, sx, 1.0, &p0); } else if (handle->id == HANDLE_RESIZE_E) { /* scale width */ Point p0 = stdpath->handles[3].pos; /* W */ Point p1 = stdpath->handles[4].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); g_assert(stdpath->handles[4].id == handle->id); g_assert(stdpath->handles[3].id == HANDLE_RESIZE_W); if (to->x - EPSILON < p0.x) sx = 1.0; _stdpath_scale (stdpath, sx, 1.0, &p0); } else if (handle->id == HANDLE_RESIZE_SW) { Point p0 = stdpath->handles[2].pos; /* NE */ Point p1 = stdpath->handles[5].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[5].id == handle->id); g_assert(stdpath->handles[2].id == HANDLE_RESIZE_NE); if (to->x + EPSILON > p0.x) sx = 1.0; if (to->y - EPSILON < p0.y) sy = 1.0; _stdpath_scale (stdpath, sx, sy, &p0); } else if (handle->id == HANDLE_RESIZE_S) { /* scale height */ Point p0 = stdpath->handles[1].pos; /* N */ Point p1 = stdpath->handles[6].pos; real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[6].id == handle->id); g_assert(stdpath->handles[1].id == HANDLE_RESIZE_N); if (to->y - EPSILON < p0.y) sy = 1.0; _stdpath_scale (stdpath, 1.0, sy, &p0); } else if (handle->id == HANDLE_RESIZE_SE) { /* scale both directions - keep aspect ratio? */ Point p0 = stdpath->handles[0].pos; /* NW */ Point p1 = stdpath->handles[7].pos; real sx = (to->x - p0.x) / (p1.x - p0.x); real sy = (to->y - p0.y) / (p1.y - p0.y); g_assert(stdpath->handles[7].id == handle->id); g_assert(stdpath->handles[0].id == HANDLE_RESIZE_NW); if (to->x - EPSILON < p0.x) sx = 1.0; if (to->y - EPSILON < p0.y) sy = 1.0; _stdpath_scale (stdpath, sx, sy, &p0); } else if (handle->type != HANDLE_NON_MOVABLE) { g_warning ("stdpath_move_handle() %d not moving", handle->id); } stdpath_update_data (stdpath); return NULL; }