static void spdc_pen_set_point (SPPenContext *pc, NRPointF *p, guint state) { SPDrawContext *dc; dc = SP_DRAW_CONTEXT (pc); if (dc->npoints == 0) { /* Just set initial point */ dc->p[0] = *p; dc->p[1] = *p; dc->npoints = 2; sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); } else { dc->p[2] = *p; dc->p[3] = *p; dc->p[4] = *p; dc->npoints = 5; sp_curve_reset (dc->red_curve); sp_curve_moveto (dc->red_curve, dc->p[0].x, dc->p[0].y); if ((pc->onlycurves) || (dc->p[1].x != dc->p[0].x) || (dc->p[1].y != dc->p[0].y)) { sp_curve_curveto (dc->red_curve, dc->p[1].x, dc->p[1].y, dc->p[2].x, dc->p[2].y, dc->p[3].x, dc->p[3].y); } else { sp_curve_lineto (dc->red_curve, dc->p[3].x, dc->p[3].y); } sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), dc->red_curve); } }
static void interpolate(SPPencilContext *pc) { g_assert( pc->ps.size() > 1 ); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double const tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4; double const tolerance_sq = 0.02 * square( pc->desktop->w2d().descrim() * tol) * exp(0.2*tol - 2); g_assert(is_zero(pc->req_tangent) || is_unit_vector(pc->req_tangent)); Geom::Point const tHatEnd(0, 0); guint n_points = pc->ps.size(); pc->green_curve->reset(); pc->red_curve->reset(); pc->red_curve_is_valid = false; Geom::Point * b = g_new(Geom::Point, 4*n_points); Geom::Point * points = g_new(Geom::Point, 4*n_points); for (unsigned int i = 0; i < pc->ps.size(); i++) { points[i] = pc->ps[i]; } // worst case gives us a segment per point int max_segs = 4*n_points; int const n_segs = Geom::bezier_fit_cubic_r(b, points, n_points, tolerance_sq, max_segs); if ( n_segs > 0) { /* Fit and draw and reset state */ pc->green_curve->moveto(b[0]); for (int c = 0; c < n_segs; c++) { pc->green_curve->curveto(b[4*c+1], b[4*c+2], b[4*c+3]); } sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), pc->green_curve); /* Fit and draw and copy last point */ g_assert(!pc->green_curve->is_empty()); /* Set up direction of next curve. */ { Geom::CubicBezier const * last_seg = dynamic_cast<Geom::CubicBezier const *>(pc->green_curve->last_segment()); g_assert( last_seg ); // Relevance: validity of (*last_seg)[2] pc->p[0] = last_seg->finalPoint(); pc->npoints = 1; Geom::Point const req_vec( pc->p[0] - (*last_seg)[2] ); pc->req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) ) ? Geom::Point(0, 0) : Geom::unit_vector(req_vec) ); } } g_free(b); g_free(points); pc->ps.clear(); }
/** * Change moving endpoint position. * <ul> * <li>Ctrl constrains to moving to H/V direction, snapping in given direction. * <li>Otherwise we snap freely to whatever attractors are available. * </ul> * * Number of points is (re)set to 2 always, 2nd point is modified. * We change RED curve. */ static void spdc_set_endpoint(SPPencilContext *const pc, Geom::Point const p) { if (pc->npoints == 0) { return; /* May occur if first point wasn't in SVG plane (e.g. weird w2d transform, perhaps from bad * zoom setting). */ } g_return_if_fail( pc->npoints > 0 ); pc->red_curve->reset(); if ( ( p == pc->p[0] ) || !in_svg_plane(p) ) { pc->npoints = 1; } else { pc->p[1] = p; pc->npoints = 2; pc->red_curve->moveto(pc->p[0]); pc->red_curve->lineto(pc->p[1]); pc->red_curve_is_valid = true; sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), pc->red_curve); } }
static void pencil_cancel (SPPencilContext *const pc) { if (pc->grab) { /* Release grab now */ sp_canvas_item_ungrab(pc->grab, 0); pc->grab = NULL; } pc->is_drawing = false; pc->state = SP_PENCIL_CONTEXT_IDLE; pc->red_curve->reset(); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), NULL); while (pc->green_bpaths) { gtk_object_destroy(GTK_OBJECT(pc->green_bpaths->data)); pc->green_bpaths = g_slist_remove(pc->green_bpaths, pc->green_bpaths->data); } pc->green_curve->reset(); if (pc->green_anchor) { pc->green_anchor = sp_draw_anchor_destroy(pc->green_anchor); } pc->_message_context->clear(); pc->_message_context->flash(Inkscape::NORMAL_MESSAGE, _("Drawing cancelled")); sp_canvas_end_forced_full_redraws(pc->desktop->canvas); }
static void fit_and_split(SPPencilContext *pc) { g_assert( pc->npoints > 1 ); double const tolerance_sq = 0; Geom::Point b[4]; g_assert(is_zero(pc->req_tangent) || is_unit_vector(pc->req_tangent)); Geom::Point const tHatEnd(0, 0); int const n_segs = Geom::bezier_fit_cubic_full(b, NULL, pc->p, pc->npoints, pc->req_tangent, tHatEnd, tolerance_sq, 1); if ( n_segs > 0 && unsigned(pc->npoints) < G_N_ELEMENTS(pc->p) ) { /* Fit and draw and reset state */ pc->red_curve->reset(); pc->red_curve->moveto(b[0]); pc->red_curve->curveto(b[1], b[2], b[3]); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), pc->red_curve); pc->red_curve_is_valid = true; } else { /* Fit and draw and copy last point */ g_assert(!pc->red_curve->is_empty()); /* Set up direction of next curve. */ { Geom::CubicBezier const * last_seg = dynamic_cast<Geom::CubicBezier const *>(pc->red_curve->last_segment()); g_assert( last_seg ); // Relevance: validity of (*last_seg)[2] pc->p[0] = last_seg->finalPoint(); pc->npoints = 1; Geom::Point const req_vec( pc->p[0] - (*last_seg)[2] ); pc->req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) ) ? Geom::Point(0, 0) : Geom::unit_vector(req_vec) ); } pc->green_curve->append_continuous(pc->red_curve, 0.0625); SPCurve *curve = pc->red_curve->copy(); /// \todo fixme: SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(pc->desktop), curve); curve->unref(); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), pc->green_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); pc->green_bpaths = g_slist_prepend(pc->green_bpaths, cshape); pc->red_curve_is_valid = false; } }
SPCanvasItem * sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve) { g_return_val_if_fail (parent != NULL, NULL); g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL); SPCanvasItem *item = sp_canvas_item_new (parent, SP_TYPE_CANVAS_BPATH, NULL); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve); return item; }
static void spdc_finish_endpoint (SPPencilContext *pc, NRPointF *p, gboolean snap, guint state) { SPDrawContext *dc; dc = SP_DRAW_CONTEXT (pc); if (SP_CURVE_LENGTH (dc->red_curve) < 2) { /* Just a click, reset red curve and continue */ g_print ("Finishing empty red curve\n"); sp_curve_reset (dc->red_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); } else if (SP_CURVE_LENGTH (dc->red_curve) > 2) { g_warning ("Red curve length is %d", SP_CURVE_LENGTH (dc->red_curve)); sp_curve_reset (dc->red_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); } else { ArtBpath *s, *e; /* We have actual line */ if (snap) { /* Do (bogus?) snap */ spdc_endpoint_snap (dc, p, state); } /* fixme: We really should test start and end anchors instead */ s = SP_CURVE_SEGMENT (dc->red_curve, 0); e = SP_CURVE_SEGMENT (dc->red_curve, 1); if ((e->x3 == s->x3) && (e->y3 == s->y3)) { /* Empty line, reset red curve and continue */ g_print ("Finishing zero length red curve\n"); sp_curve_reset (dc->red_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); } else { /* Write curves to object */ g_print ("Finishing real red curve\n"); spdc_concat_colors_and_flush (dc, FALSE); dc->sa = NULL; dc->ea = NULL; } } }
static void spdc_reset_colors (SPDrawContext *dc) { /* Red */ sp_curve_reset (dc->red_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); /* Blue */ sp_curve_reset (dc->blue_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->blue_bpath), NULL); /* Green */ while (dc->green_bpaths) { gtk_object_destroy (GTK_OBJECT (dc->green_bpaths->data)); dc->green_bpaths = g_slist_remove (dc->green_bpaths, dc->green_bpaths->data); } sp_curve_reset (dc->green_curve); if (dc->green_anchor) { dc->green_anchor = sp_draw_anchor_destroy (dc->green_anchor); } dc->sa = NULL; dc->ea = NULL; dc->npoints = 0; }
/** * Finalize addline. * * \todo * fixme: I'd like remove red reset from concat colors (lauris). * Still not sure, how it will make most sense. */ static void spdc_finish_endpoint(SPPencilContext *const pc) { if ( ( pc->red_curve->is_empty() ) || ( *(pc->red_curve->first_point()) == *(pc->red_curve->second_point()) ) ) { pc->red_curve->reset(); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), NULL); } else { /* Write curves to object. */ spdc_concat_colors_and_flush(pc, FALSE); pc->sa = NULL; pc->ea = NULL; } }
void Inkscape::Rubberband::move(Geom::Point const &p) { if (!_started) return; _end = p; _desktop->scroll_to_point(p); _touchpath_curve->lineto(p); Geom::Point next = _desktop->d2w(p); // we want the points to be at most 0.5 screen pixels apart, // so that we don't lose anything small; // if they are farther apart, we interpolate more points if (_points.size() > 0 && Geom::L2(next-_points.back()) > 0.5) { Geom::Point prev = _points.back(); int subdiv = 2 * (int) round(Geom::L2(next-prev) + 0.5); for (int i = 1; i <= subdiv; i ++) { _points.push_back(prev + ((double)i/subdiv) * (next - prev)); } } else { _points.push_back(next); } if (_mode == RUBBERBAND_MODE_RECT) { if (_rect == NULL) { _rect = static_cast<CtrlRect *>(sp_canvas_item_new(sp_desktop_controls(_desktop), SP_TYPE_CTRLRECT, NULL)); } _rect->setRectangle(Geom::Rect(_start, _end)); sp_canvas_item_show(_rect); if (_touchpath) sp_canvas_item_hide(_touchpath); } else if (_mode == RUBBERBAND_MODE_TOUCHPATH) { if (_touchpath == NULL) { _touchpath = sp_canvas_bpath_new(sp_desktop_sketch(_desktop), NULL); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(_touchpath), 0xff0000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(_touchpath), 0, SP_WIND_RULE_NONZERO); } sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(_touchpath), _touchpath_curve); sp_canvas_item_show(_touchpath); if (_rect) sp_canvas_item_hide(_rect); } }
static void spdc_set_endpoint (SPPencilContext *pc, NRPointF *p, guint state) { SPDrawContext *dc; dc = SP_DRAW_CONTEXT (pc); g_assert (dc->npoints > 0); spdc_endpoint_snap (dc, p, state); dc->p[1] = *p; dc->npoints = 2; sp_curve_reset (dc->red_curve); sp_curve_moveto (dc->red_curve, dc->p[0].x, dc->p[0].y); sp_curve_lineto (dc->red_curve, dc->p[1].x, dc->p[1].y); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), dc->red_curve); }
static void spdc_pen_set_ctrl (SPPenContext *pc, NRPointF *p, guint state) { SPDrawContext *dc; dc = SP_DRAW_CONTEXT (pc); sp_canvas_item_show (pc->c1); sp_canvas_item_show (pc->cl1); if (dc->npoints == 2) { dc->p[1] = *p; sp_canvas_item_hide (pc->c0); sp_canvas_item_hide (pc->cl0); sp_ctrl_moveto (SP_CTRL (pc->c1), dc->p[1].x, dc->p[1].y); sp_ctrlline_set_coords (SP_CTRLLINE (pc->cl1), dc->p[0].x, dc->p[0].y, dc->p[1].x, dc->p[1].y); } else if (dc->npoints == 5) { dc->p[4] = *p; sp_canvas_item_show (pc->c0); sp_canvas_item_show (pc->cl0); if (((pc->mode == SP_PEN_CONTEXT_MODE_CLICK) && (state & GDK_CONTROL_MASK)) || ((pc->mode == SP_PEN_CONTEXT_MODE_DRAG) && !(state & GDK_MOD1_MASK))) { gdouble dx, dy; dx = p->x - dc->p[3].x; dy = p->y - dc->p[3].y; dc->p[2].x = dc->p[3].x - dx; dc->p[2].y = dc->p[3].y - dy; sp_curve_reset (dc->red_curve); sp_curve_moveto (dc->red_curve, dc->p[0].x, dc->p[0].y); sp_curve_curveto (dc->red_curve, dc->p[1].x, dc->p[1].y, dc->p[2].x, dc->p[2].y, dc->p[3].x, dc->p[3].y); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), dc->red_curve); } sp_ctrl_moveto (SP_CTRL (pc->c0), dc->p[2].x, dc->p[2].y); sp_ctrlline_set_coords (SP_CTRLLINE (pc->cl0), dc->p[3].x, dc->p[3].y, dc->p[2].x, dc->p[2].y); sp_ctrl_moveto (SP_CTRL (pc->c1), dc->p[4].x, dc->p[4].y); sp_ctrlline_set_coords (SP_CTRLLINE (pc->cl1), dc->p[3].x, dc->p[3].y, dc->p[4].x, dc->p[4].y); } else { g_warning ("Something bad happened - npoints is %d", dc->npoints); } }
static void fit_and_split (SPDrawContext * dc) { NRPointF b[4]; gdouble tolerance; g_assert (dc->npoints > 1); tolerance = SP_EVENT_CONTEXT (dc)->desktop->w2d[0] * TOLERANCE; tolerance = tolerance * tolerance; if (sp_bezier_fit_cubic (b, dc->p, dc->npoints, tolerance) > 0 && dc->npoints < SP_DRAW_POINTS_MAX) { /* Fit and draw and reset state */ sp_curve_reset (dc->red_curve); sp_curve_moveto (dc->red_curve, b[0].x, b[0].y); sp_curve_curveto (dc->red_curve, b[1].x, b[1].y, b[2].x, b[2].y, b[3].x, b[3].y); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), dc->red_curve); } else { SPCurve *curve; SPCanvasItem *cshape; /* Fit and draw and copy last point */ g_assert (!sp_curve_empty (dc->red_curve)); sp_curve_append_continuous (dc->green_curve, dc->red_curve, 0.0625); curve = sp_curve_copy (dc->red_curve); /* fixme: */ cshape = sp_canvas_bpath_new (SP_DT_SKETCH (SP_EVENT_CONTEXT (dc)->desktop), curve); sp_curve_unref (curve); sp_canvas_bpath_set_stroke (SP_CANVAS_BPATH (cshape), dc->green_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); dc->green_bpaths = g_slist_prepend (dc->green_bpaths, cshape); dc->p[0] = dc->p[dc->npoints - 2]; dc->p[1] = dc->p[dc->npoints - 1]; dc->npoints = 2; } }
/* interpolates the sketched curve and tweaks the current sketch interpolation*/ static void sketch_interpolate(SPPencilContext *pc) { g_assert( pc->ps.size() > 1 ); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double const tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4; double const tolerance_sq = 0.02 * square( pc->desktop->w2d().descrim() * tol) * exp(0.2*tol - 2); bool average_all_sketches = prefs->getBool("/tools/freehand/pencil/average_all_sketches", true); g_assert(is_zero(pc->req_tangent) || is_unit_vector(pc->req_tangent)); Geom::Point const tHatEnd(0, 0); guint n_points = pc->ps.size(); pc->red_curve->reset(); pc->red_curve_is_valid = false; Geom::Point * b = g_new(Geom::Point, 4*n_points); Geom::Point * points = g_new(Geom::Point, 4*n_points); for (unsigned i = 0; i < pc->ps.size(); i++) { points[i] = pc->ps[i]; } // worst case gives us a segment per point int max_segs = 4*n_points; int const n_segs = Geom::bezier_fit_cubic_r(b, points, n_points, tolerance_sq, max_segs); if ( n_segs > 0) { Geom::Path fit(b[0]); for (int c = 0; c < n_segs; c++) { fit.appendNew<Geom::CubicBezier>(b[4*c+1], b[4*c+2], b[4*c+3]); } Geom::Piecewise<Geom::D2<Geom::SBasis> > fit_pwd2 = fit.toPwSb(); double t =0.; if ( pc->sketch_n > 0 ) { if (average_all_sketches) { // Average = (sum of all) / n // = (sum of all + new one) / n+1 // = ((old average)*n + new one) / n+1 t = pc->sketch_n / (pc->sketch_n + 1.); } else { t = 0.5; } pc->sketch_interpolation = Geom::lerp(t, fit_pwd2, pc->sketch_interpolation); } else { pc->sketch_interpolation = fit_pwd2; } pc->sketch_n++; pc->green_curve->reset(); pc->green_curve->set_pathvector(Geom::path_from_piecewise(pc->sketch_interpolation, 0.01)); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), pc->green_curve); /* Fit and draw and copy last point */ g_assert(!pc->green_curve->is_empty()); /* Set up direction of next curve. */ { Geom::CubicBezier const * last_seg = dynamic_cast<Geom::CubicBezier const *>(pc->green_curve->last_segment()); g_assert( last_seg ); // Relevance: validity of (*last_seg)[2] pc->p[0] = last_seg->finalPoint(); pc->npoints = 1; Geom::Point const req_vec( pc->p[0] - (*last_seg)[2] ); pc->req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) ) ? Geom::Point(0, 0) : Geom::unit_vector(req_vec) ); } } g_free(b); g_free(points); pc->ps.clear(); }
static void spdc_concat_colors_and_flush (SPDrawContext *dc, gboolean forceclosed) { SPCurve *c; /* Concat RBG */ c = dc->green_curve; /* Green */ dc->green_curve = sp_curve_new_sized (64); while (dc->green_bpaths) { gtk_object_destroy (GTK_OBJECT (dc->green_bpaths->data)); dc->green_bpaths = g_slist_remove (dc->green_bpaths, dc->green_bpaths->data); } /* Blue */ sp_curve_append_continuous (c, dc->blue_curve, 0.0625); sp_curve_reset (dc->blue_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->blue_bpath), NULL); /* Red */ sp_curve_append_continuous (c, dc->red_curve, 0.0625); sp_curve_reset (dc->red_curve); sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (dc->red_bpath), NULL); /* Step A - test, whether we ended on green anchor */ if (forceclosed || (dc->green_anchor && dc->green_anchor->active)) { g_print ("We hit green anchor, closing Green-Blue-Red\n"); sp_curve_closepath_current (c); /* Closed path, just flush */ spdc_flush_white (dc, c); sp_curve_unref (c); return; } /* Step B - both start and end anchored to same curve */ if (dc->sa && dc->ea && (dc->sa->curve == dc->ea->curve)) { g_print ("We hit bot start and end of single curve, closing paths\n"); if (dc->sa->start) { SPCurve *r; g_print ("Reversing curve\n"); r = sp_curve_reverse (c); sp_curve_unref (c); c = r; } sp_curve_append_continuous (dc->sa->curve, c, 0.0625); sp_curve_unref (c); sp_curve_closepath_current (dc->sa->curve); spdc_flush_white (dc, NULL); return; } /* Step C - test start */ if (dc->sa) { SPCurve *s; g_print ("Curve start hit anchor\n"); s = dc->sa->curve; dc->white_curves = g_slist_remove (dc->white_curves, s); if (dc->sa->start) { SPCurve *r; g_print ("Reversing curve\n"); r = sp_curve_reverse (s); sp_curve_unref (s); s = r; } sp_curve_append_continuous (s, c, 0.0625); sp_curve_unref (c); c = s; } /* Step D - test end */ if (dc->ea) { SPCurve *e; g_print ("Curve end hit anchor\n"); e = dc->ea->curve; dc->white_curves = g_slist_remove (dc->white_curves, e); if (!dc->ea->start) { SPCurve *r; g_print ("Reversing curve\n"); r = sp_curve_reverse (e); sp_curve_unref (e); e = r; } sp_curve_append_continuous (c, e, 0.0625); sp_curve_unref (e); } spdc_flush_white (dc, c); sp_curve_unref (c); }