int TRACE::tap_cb(CGESTUREptr& gest, DrawState*& s) { assert (gest->is_tap()); if (_calib_mode_flag) { // we are in calibration point XYpt sample = gest->end(); _samples[_cur_calib_pt_index] = sample; cerr << "recorded sample " << _cur_calib_pt_index << endl; _cur_calib_pt_index++; /* seems impractical to distort image, leaving this at 2 - alexni if (_cur_calib_pt_index == 4) { */ if (_cur_calib_pt_index == 2) { // if we have sampled four points, we are done with calibration _calib_mode_flag = false; _calibrated = true; WORLD::message("Calibration complete"); } return 1; } else return 0; }
int CIRCLE_WIDGET::tap_cb(CGESTUREptr& g, DrawState*& s) { err_adv(debug_all, "CIRCLE_WIDGET::tap_cb()"); PIXEL pdummy; int idummy; if( _suggest_active ) { _preview.update_length(); _literal_shape.update_length(); double preview_dist = PIXEL_list(_preview).closest( PIXEL(g->start()), pdummy, idummy ); double literal_dist = PIXEL_list(_literal_shape).closest( PIXEL(g->start()), pdummy, idummy ); if( preview_dist < literal_dist && preview_dist < PIXEL_DIST_THRESH ) { _suggest_active = false; make_preview(); return 1; } else if( literal_dist < preview_dist && literal_dist < PIXEL_DIST_THRESH ) { finish_literal(); return cancel_cb(g,s); } else { return cancel_cb(g,s); } } if (_cmd) WORLD::add_command(_cmd); return cancel_cb(g,s); }
//! Draw a straight line cross-ways to an existing //! straight Bcurve to create a rectangle: bool SWEEP_LINE::create_rect(CGESTUREptr& gest) { static bool debug = Config::get_var_bool("DEBUG_CREATE_RECT",false) || debug_all; err_adv(debug, "SWEEP_LINE::create_rect"); if (!(gest && gest->is_line())) { err_adv(debug, "SWEEP_LINE::create_rect: gesture is not a line"); return false; } // central axis must be on-screen if (!sweep_origin().in_frustum()) { err_adv(debug_all, "SWEEP_LINE::stroke_cb: error: sweep origin off-screen"); return false; } if (!from_center(gest)) { WORLD::message("Stroke must begin on curve at guideline"); return false; } if (!hits_line(gest->end())) { WORLD::message("Line must follow axis"); return false; } // compute vector along guideline, based on input stroke: Wvec v = project_to_guideline(gest->end()) - sweep_origin(); return create_rect(v); }
int SELECT_WIDGET::tap_cb(CGESTUREptr& g, DrawState*& s) { err_adv(debug, "SELECT_WIDGET::tap_cb()"); assert(g && g->is_tap()); if (_mode==SLASH_SEL) { //pattern editing Bface* f = find_face(g->start(),0.25,MIN_PIX_AREA); if (f) { if (select_list.contains(f)||select_list.contains(f->quad_partner())) { //get whichever part of the quad is in the selection list int temp = select_list.contains(f) ? select_list.get_index(f)+1 : select_list.get_index(f->quad_partner())+1 ; if (temp>end_face) //user selected the end face { end_face=temp; } else //user is selecting a pattern { if (pattern<temp) pattern=temp; //select/deselect face if (temp < MAX_PATTERN_SIZE) pattern_array[temp]=!pattern_array[temp]; } return 1; } else cerr << "tap found a NULL face !" << endl; } return cancel_cb(g,s); } else { // Tap a selected face near the middle to deselect it: if (try_deselect_face(g->center(), 0.25)) return 1; // Tap edge to deselect if (try_deselect_edge(g->center())) return 1; } // Otherwise, turn off SELECT_WIDGET return cancel_cb(g,s); }
//! Given a set of enclosed face, activate the widget to sweep out a //! shape. Checks for errors, returns true on success. bool SWEEP_DISK::setup(CGESTUREptr& gest, double dur) { static bool debug = Config::get_var_bool("DEBUG_SWEEP_SETUP",false) || debug_all; if (!(gest && gest->is_dslash())) { err_adv(debug, "SWEEP_DISK::setup: bad gesture"); return false; } // XXX - shouldn't require it is a Panel: Panel* p = dynamic_cast<Panel*>(Bsurface::hit_ctrl_surface(gest->start())); if (!p) { err_adv(debug, "SWEEP_DISK::setup: non-panel"); return false; } Bface_list faces = p->bfaces(); _boundary = faces.get_boundary(); if (_boundary.num_line_strips() != 1) { err_adv(debug, "SWEEP_DISK::setup: error: boundary is not a single piece"); return false; } // Get the best-fit plane, rejecting if the boundary Wpt_list // doesn't lie within 0.1 of its total length from the plane: if (!_boundary.verts().pts().get_plane(_plane, 0.1)) { err_adv(debug,"SWEEP_DISK::setup: Error: can't find plane"); return false; } // Find the center Wpt o = _boundary.verts().pts().average(); // decide guideline direction (normal to plane): Wvec n = _plane.normal(); if (VIEW::eye_vec(o) * n > 0) n = -n; // decide the length for the guideline: double len = world_length(o, GUIDE_LEN); // compute guideline endpoint: Wpt b = o + n.normalized()*len; // try basic setup if (!SWEEP_BASE::setup(dynamic_pointer_cast<LMESH>(faces.mesh()), o, b, dur)) return false; // ******** From here on we accept it ******** _enclosed_faces = faces; return true; }
int SELECT_WIDGET::stroke_cb(CGESTUREptr& g, DrawState*&) { assert(g); select_faces(g->pts()) || select_edges(g->pts()); return 1; }
int SELECT_WIDGET::sm_circle_cb(CGESTUREptr& g, DrawState*& s) { err_adv(debug, "SELECT_WIDGET::sm_circle_cb()"); try_select_face(g->center(), 0.25) || try_select_edge(g->center()); return 1; }
int XformPen::tap_cb(CGESTUREptr& tap, DrawState*& s) { assert(tap); if (tap->is_double_tap()) { // should never happen given order of arcs in XformPen constructor cerr << "XformPen::tap_cb: error: gesture is double tap" << endl; return 0; } // tap on cursor? BMESHray ray(tap->center()); _view->intersect(ray); Cursor3D* c = Cursor3D::upcast(ray.geom()); if (c) { err_adv(debug, "XformPen::tap_cb: hit axis"); c->handle_gesture(tap); return 0; } // tap on mesh? _mesh = 0; Bface* f = cur_face(); if (!f) { err_adv(debug, "XformPen::tap_cb: missed face"); return cancel_cb(tap, s); } BMESH* m = f->mesh(); if (!m) { err_adv(debug, "XformPen::tap_cb: hit face, no mesh"); return cancel_cb(tap, s); } GEOMptr g = bmesh_to_geom(m); if (!g) { err_adv(debug, "XformPen::tap_cb: hit mesh, no geom"); return cancel_cb(tap, s); } // skip floor: if (FLOOR::isa(g)) { err_adv(debug, "XformPen::tap_cb: hit floor, skipping..."); return cancel_cb(tap, s); } // tap on ordinary mesh (not floor): _mesh = m; assert(_mesh); BMESH::set_focus(_mesh, f->patch()); FLOOR::show(); FLOOR::realign(_mesh,0); // 0 = no undo command Cursor3D::attach(g); return 0; }
int INFLATE::stroke_cb(CGESTUREptr& gest, DrawState*& s) { err_adv(debug, "INFLATE::stroke_cb"); reset_timeout(); // Verify that we have a starting face if ( _orig_face ) { Bface* face = 0; // Check that the stroke is straight enough to represent a line if (!(gest->straightness() > 0.8)) { err_adv(debug, "INFLATE::stroke_cb: gesture not straight"); return false; } // Check that the gesture starts on the mesh Bsurface::hit_ctrl_surface(gest->start(), 1, &face); if (!(face)) { err_adv(debug, "INFLATE::stroke_cb: can't get hit face"); return false; } // create VEXELs for the gesture and the face normal VEXEL fvec = VEXEL(face->v1()->loc(), face->norm()); VEXEL fgest = gest->endpt_vec(); // If gesture nearly parallel to normal: double a = rad2deg(line_angle(fvec,fgest)); err_adv(debug, "INFLATE::stroke_cb: angle: %f %s", a, (a > 15) ? "(bad)" : "(good)"); if (a > 15) { // Fail if angle is too extreme WORLD::message("Bad angle"); return 0; } // calculate extrude width double dist = fgest.length()/fvec.length(); err_adv(debug, "INFLATE::stroke_cb: strong_edge_len: %f, gest_len: %f, \ fvect_len: %f", avg_strong_edge_len(face), fgest.length(), fvec.length() ); // Convert to relative to local edge length dist /= avg_strong_edge_len(face); if (fvec*fgest<0) dist=-dist; // Get the sign right // Store the new inflate distance _preview_dist = dist; } return 1; // we used up the gesture... }
bool PAPER_DOLL::init(CGESTUREptr& g) { if (!(g && g->is_stroke())) return false; if (g->below_min_length() || g->below_min_spread()) return false; if (!g->is_ellipse()) { err_adv(debug, "PAPER_DOLL::init: non-ellipse"); return false; } Panel* p = dynamic_cast<Panel*>( Bsurface::get_surface(get_top_level(VisRefImage::get_faces(g->pts()))) ); err_adv(debug, "PAPER_DOLL::init: %s panel", p?"found":"could not find"); if (!(p && p->is_selected())) { err_adv(debug, "PAPER_DOLL::init: ellipse not over selected panel"); return false; } assert(p && p->bfaces().size() > 0); Bface_list faces = Bface_list::reachable_faces(p->bfaces().front()); assert(!faces.empty()); if (!faces.is_planar(deg2rad(1.0))) { err_adv(debug, "PAPER_DOLL::init: region is not planar"); return false; } EdgeStrip boundary = faces.get_boundary(); if (boundary.empty()) { err_adv(debug, "PAPER_DOLL::init: region has no boundary"); return false; } Bsurface_list surfs = Bsurface::get_surfaces(faces); if (!are_all_bsurfaces<Panel>(surfs)) { err_adv(debug, "PAPER_DOLL::init: region not all panels"); return 0; } err_adv(debug, "PAPER_DOLL::init: proceeding..."); err_adv(debug, " boundary edges: %d, components: %d, panels: %d", boundary.edges().size(), boundary.num_line_strips(), surfs.num() ); if (get_instance()->build_primitive(faces)) { err_adv(debug, " ... succeeded"); return true; } err_adv(debug, " ... failed"); return false; }
//! Currently checks to see that the gesture was a small circle. //! If so, this may be an indication that the user wants to go //! into edge selection mode. It then executes try_select_edge() //! to try to find the edge. When successful, the widget enters //! edge selection mode. bool SELECT_WIDGET::init_select_edge(CGESTUREptr& g) { if (!g->is_small_circle()) return false; if (!try_select_edge(g->center())) return false; _mode = SEL_EDGE; reset_timeout(); activate(); return true; }
void Pen::notify_gesture(GEST_INT* gi, CGESTUREptr& gest) { if (Config::get_var_bool("DEBUG_GESTURES",false)) { gest->print_stats(); gest->print_types(); } _fsa.handle_event(gest); if (_fsa.is_reset()) gi->reset(); }
int INFLATE::line_cb(CGESTUREptr& gest, DrawState*& s) { // Activity occurred to extend the deadline for fading away: reset_timeout(); err_adv(debug, "INFLATE::line_cb"); // Verify that we have a starting face if ( _orig_face ) { // Check that the stroke is straight enough to represent a line Bface* face = 0; if (!(gest->straightness() > 0.8)) { err_adv(debug, "INFLATE::line_cb: gesture not straight"); return false; } // Check that the gesture starts on the mesh Bsurface::hit_ctrl_surface(gest->start(), 1, &face); if (!(face)) { err_adv(debug, "INFLATE::line_cb: can't get hit face"); return false; } // create VEXELs for the gesture and the face normal VEXEL fvec = VEXEL(face->v1()->loc(), face->norm()); VEXEL fgest = gest->endpt_vec(); // If gesture nearly parallel to normal: double a = rad2deg(line_angle(fvec,fgest)); err_adv(debug, "INFLATE::line_cb: angle: %f %s", a, (a > 15) ? "(bad)" : "(good)"); if (a > 15) { return false; } // calculate extrude width double dist = fgest.length()/fvec.length(); if (fvec*fgest<0) dist=-dist; // Get the sign right _preview_dist = dist; } // get here if nothing happened... // don't cancel (which would deactivate the widget), // just return 1 to indicate that we used up the gesture: return 1; }
bool OVERSKETCH::find_matching_sil(CGESTUREptr& g) { err_adv(debug, "OVERSKETCH::find_matching_sil"); const size_t MIN_GEST_PTS = 10; if (!(g && g->pts().size() >= MIN_GEST_PTS)) return false; if (BMESH::_freeze_sils) return false; VisRefImage *vis_ref = VisRefImage::lookup(VIEW::peek()); if (!vis_ref) return false; // 1. see if the gesture runs along a silhouette // of a single mesh. SilEdgeFilter sil_filter; const PIXEL_list& pts = g->pts(); BMESHptr mesh = nullptr; for (PIXEL_list::size_type i=0; i<pts.size(); i++) { Bedge* e = (Bedge*) vis_ref->find_near_simplex(pts[i], SIL_SEARCH_RAD, sil_filter); if (!(e && e->mesh())) { err_adv(debug, " gesture too far from silhouette"); return false; } if (mesh && mesh != e->mesh()) { err_adv(debug, " found a second mesh, rejecting"); return false; } mesh = e->mesh(); } if (!dynamic_pointer_cast<LMESH>(mesh)) { err_adv(debug, " found non-LMESH, rejecting"); return false; } err_adv(debug, " gesture aligns with silhouette"); err_adv(debug, " mesh level %d", mesh->subdiv_level()); // 2. extract the portion of the silhouette that matches // the gesture, store in _selected_sils return find_matching_sil(pts, mesh->sil_strip()); }
bool OVERSKETCH::init(CGESTUREptr& g) { if (!(g && g->is_stroke())) return false; if (g->below_min_length() || g->below_min_spread()) return false; if (get_instance()->find_matching_sil(g)) return true; // add more cases here... return false; }
int NPRPen::tap_cb(CGESTUREptr& gest, DrawState*&) { cerr << "NPRPen::tap_cb" << endl; Bface *f = VisRefImage::Intersect(gest->center()); Patch* p = get_ctrl_patch(f); if (!(f && p)) { if (_curr_npr_tex) deselect_current_texture(); return 0; } // Set the selected face's patch to using NPRTexture // It might already be doing this, but who cares! p->set_texture(NPRTexture::static_name()); GTexture* cur_texture = p->cur_tex(); NPRTexture* nt = dynamic_cast<NPRTexture*>(cur_texture); if (nt == nullptr) return 0; //Shouldn't happen if (_curr_npr_tex) { if (_curr_npr_tex != nt) { deselect_current_texture(); select_current_texture(nt); } } else select_current_texture(nt); return 0; }
int SWEEP_BASE::trim_line_cb(CGESTUREptr& g, DrawState*&) { // Activity occurred to extend the deadline for fading away: reset_timeout(); // A slash gesture across the guideline trims its tip static bool debug = Config::get_var_bool("DEBUG_SWEEP_TRIM",false) || debug_all; err_adv(debug, "SWEEP_BASE::trim_cb"); // Find where the slash intersects the guideline. PIXEL p; if (g->endpt_line().intersect_segs(pix_line(), p)) { // Reset the endpoint to match screen position p: reset_endpoint(p); return 1; } // The slash gesture missed the guideline... // XXX - policy on failure? err_adv(debug, "SWEEP_BASE::trim_line_cb: Missed the guideline"); return 1; // This means we DID use up the gesture }
bool PatternPen::compute_target_cell(CGESTUREptr& gest){ if (_target_cell) { print_target_cell(); } int nb_pts = gest->pts().num(); if (nb_pts < 2) return false; if (_current_cell_type == BBOX_CELL){ _target_cell = new BBoxCell(gest->pts()[0], gest->pts()[nb_pts-1]); } else if (_current_cell_type == RECT_CELL){ double thickness; GESTUREptr axis_gesture; update_rect_drawer(gest, axis_gesture, thickness); if (axis_gesture) { _target_cell = new RectCell(axis_gesture, thickness); } else { return false; } } else if (_current_cell_type == PATH_CELL){ double thickness; GESTUREptr axis_gesture; update_path_drawer(gest, axis_gesture, thickness); if (axis_gesture) { _target_cell = new PathCell(axis_gesture, thickness); } else { return false; } } else if (_current_cell_type == CARRIER_CELL){ GESTUREptr first_gesture; GESTUREptr second_gesture; update_carrier_drawer(gest, first_gesture, second_gesture); if (first_gesture) { _target_cell = new CarriersCell(first_gesture, second_gesture); } else { return false; } } else { return false; } _target_cell->set_global_scale(_current_global_scale); return true; }
int SWEEP_BASE::tap_cb(CGESTUREptr& g, DrawState*& s) { err_adv(debug_all, "SWEEP_BASE::tap_cb"); // tap near end of guideline: do uniform sweep based // on guideline length: if (g->center().dist(sweep_end()) < DIST_THRESH_PIXELS) return do_uniform_sweep(sweep_vec()); // tap elsewhere on the guideline: do uniform sweep based // on tap location: if (hits_line(g->end())) return do_uniform_sweep(project_to_guideline(g->end()) - sweep_origin()); // tap elsewhere: cancel: return cancel_cb(g,s); }
int OVERSKETCH::stroke_cb(CGESTUREptr& gest, DrawState*& s) { err_adv(debug, "OVERSKETCH::stroke_cb"); assert(gest); try_oversketch(gest->pts()); return 1; // This means we did use up the gesture }
int INFLATE::tap_cb(CGESTUREptr& gest, DrawState*& state) { err_adv(debug, "INFLATE::tap_cb"); // Tracks if the tap was near a guideline bool near_guidelines = false; PIXEL pdummy; int idummy; if ( PIXEL_list(_lines).closest( PIXEL(gest->start()), pdummy, idummy ) < 5 ) near_guidelines = true; // Check if gesture hits a BFace Bface* face = 0; Bsurface::hit_ctrl_surface(gest->start(), 1, &face); // Fail if Gesture missed guidelines and geometry if ( !face && !near_guidelines ) return cancel_cb(gest,state); // Check that we are trying to inflate if ( _orig_face ) { // Find the reachable faces from the starting point Bface_list set = _mode ? _faces : Bface_list::reachable_faces(_orig_face); // verify that the user tapped a face that is part of the inflation region if ( face && !set.contains( face ) ) { return cancel_cb(gest,state); } // Attempt to inflate the surface INFLATE_CMDptr cmd = _mode ? (new INFLATE_CMD( _faces, _preview_dist )) : (new INFLATE_CMD( _orig_face, _preview_dist )); WORLD::add_command(cmd); } // On fail, cancel return cancel_cb(gest,state); }
/////////////////// // Proxy methods /////////////////// void PatternPen::add_stroke_to_proxy(CGESTUREptr& gest) { if (gest->pts().num() < 2) { WORLD::message("Failed to generate hatch stroke (too few samples)..."); return; } //NDCpt_list ndcpts = gest->pts(); //if (_pattern_texture) //_pattern_texture->proxy_surface()->add( // gest->pts(),gest->pressures(), _gesture_drawer->base_stroke_proto()); }
//! Given an initial slash gesture (or delayed slash) on a //! defined plane (FLOOR, Cursor3D, or existing Bpoint), set up //! the widget to provide a guideline for drawing a straight line. bool SWEEP_POINT::setup(CGESTUREptr& slash, double dur) { static bool debug = Config::get_var_bool("DEBUG_SWEEP_SETUP",false) || debug_all; err_adv(debug, "SWEEP_POINT::setup"); // check the gesture if (!(slash && slash->straightness() > 0.9)) { err_adv(debug, "SWEEP_POINT::setup: gesture is bad"); return false; } // find the (straight) Bpoint near slash start _point = Bpoint::hit_ctrl_point(slash->start()); return false; // return SWEEP_BASE::setup(_curve->mesh(), o, endpt, dur); }
int CIRCLE_WIDGET::stroke_cb(CGESTUREptr& g, DrawState*&) { err_adv(debug_all, "CIRCLE_WIDGET::stroke_cb()"); // Activity occurred to extend the deadline for fading away: reset_timeout(); // sanity check assert(g); if( !g->is_line() ) return 1; _preview.update_length(); if (PIXEL_list(_preview).dist(g->start()) < PIXEL_DIST_THRESH || g->start().dist(_center) < PIXEL_DIST_THRESH ) { if ( _circle ) { Bcurve *border = Bcurve::lookup(_circle->bfaces().get_boundary().edges()); if ( border != 0 ) { Wplane plane = border->plane(); _radius = _center.dist(Wpt(plane, Wline(XYpt(g->end())))); } } else { Wplane P = get_draw_plane(g->end()); if (!P.is_valid()) return 1; _radius = _center.dist(Wpt(P, Wline(XYpt(g->end())))); } make_preview(); } return 1; }
//! They want to make the guideline longer. //! It's easy. int SWEEP_BASE::extend_line_cb(CGESTUREptr& g, DrawState*&) { static bool debug = Config::get_var_bool("DEBUG_SWEEP_BASE_EXTEND",false) || debug_all; if (debug) err_msg("SWEEP_BASE::extend_line_cb"); reset_endpoint(g->end()); return 1; // This means we DID use up the gesture }
int PatternPen::tap_cb(CGESTUREptr& gest, DrawState*& s){ // err_msg("PatternPen::tap_cb()"); if (_mode == PROXY){ Bface* f; f = VisRefImage::Intersect(gest->center()); if(f) init_proxy(f); } else { return stroke_cb(gest, s); } return 1; }
void PatternPen::update_path_drawer(CGESTUREptr& gest, GESTUREptr& axis_gesture, double& thickness){ if (_path_gesture){ thickness = gest->endpoint_dist(); axis_gesture = new GESTURE(*_path_gesture); _path_gesture = GESTUREptr(); } else { _path_gesture = new GESTURE(*gest); _path_gesture->set_drawer(_blank_gesture_drawer); } _path_gesture_drawer->set_path(_path_gesture); }
int SWEEP_BASE::line_cb(CGESTUREptr& g, DrawState*& s) { // Activity occurred to extend the deadline for fading away: reset_timeout(); static bool debug = Config::get_var_bool("DEBUG_SWEEP_LINE_CB",false) || debug_all; err_adv(debug, "SWEEP_BASE::line_cb"); // If gesture aligns with guideline: // if it starts near the end and extends past the end, extend // if it starts near the beginning, do uniform sweep // If it's across the gesture, trim // If it's a trim stroke, it has to be short and run // across the guideline. const double TRIM_MAX_LEN = 65; if (g->length() < TRIM_MAX_LEN) { const double TRIM_ANGLE_THRESH = 80; // degrees double angle = line_angle(g->endpt_vec(), pix_line().direction()); if (rad2deg(angle) > TRIM_ANGLE_THRESH) { // Nice angle. But did it cross? if (g->endpt_line().intersect_segs(pix_line())) return trim_line_cb(g, s); } } // do uniform sweep if straight gesture starts at sweep origin // and ends near the guideline: if (from_center(g)) { if (hits_line(g->end())) return do_uniform_sweep(project_to_guideline(g->end()) - sweep_origin()); return stroke_cb(g,s); } // extend the guideline if straight gesture starts near guideline end // and is nearly parallel: const double ALIGN_ANGLE_THRESH = 15; // degrees if (pix_line().endpt().dist(g->start()) < DIST_THRESH_PIXELS && rad2deg(g->endpt_vec().angle(pix_line().direction())) < ALIGN_ANGLE_THRESH) return extend_line_cb(g, s); return stroke_cb(g,s); }
bool SWEEP_BASE::from_center(CGESTUREptr& g) const { assert(g != nullptr); return g->start().dist(sweep_origin()) < DIST_THRESH_PIXELS; }
//! Given an initial slash gesture (or delayed slash) near the //! center of an existing straight Bcurve, set up the widget to //! do a sweep cross-ways to the Bcurve: bool SWEEP_LINE::setup(CGESTUREptr& slash, double dur) { static bool debug = Config::get_var_bool("DEBUG_SWEEP_SETUP",false) || debug_all; err_adv(debug, "SWEEP_LINE::setup"); // check the gesture if (!(slash && slash->straightness() > 0.99)) { err_adv(debug, "SWEEP_LINE::setup: gesture is bad"); return false; } // find the (straight) Bcurve near slash start _curve = Bcurve::hit_ctrl_curve(slash->start()); if (!(_curve && _curve->is_straight())) { err_adv(debug, "SWEEP_LINE::setup: no straight curve at start"); return false; } // find endpoints Bpoint *b1 = _curve->b1(), *b2 = _curve->b2(); assert(b1 && b2); // straight curve must have endpoints // curve cannot be connected to other curves if (b1->vert()->degree() != 1 || b2->vert()->degree() != 1) { err_adv(debug, "SWEEP_LINE::setup: curve is not isolated"); return false; } // ensure the gesture starts near the center of the straight line Bcurve: { PIXEL a = b1->vert()->pix(); PIXEL b = b2->vert()->pix(); double t = (slash->start() - a).tlen(b-a); if (t < 0.35 || t > 0.65) { err_adv(debug, "SWEEP_LINE::setup: gesture not near center of line"); return false; } } // find the plane to work in _plane = check_plane(shared_plane(b1, b2)); if (!_plane.is_valid()) { err_adv(debug, "SWEEP_LINE::setup: no valid plane"); return false; } // check that slash is perpendicular to line Wpt a = b1->loc(); // endpoint at b1 Wpt b = b2->loc(); // endpoint at b2 Wvec t = b - a; // vector from endpt a to endpt b Wpt o = a + t/2; // center of straight line curve Wvec n = cross(_plane.normal(), t); // direction across line ab Wvec slash_vec = endpt_vec(slash, _plane); const double ALIGN_ANGLE_THRESH = 15; double angle = rad2deg(slash_vec.angle(n)); if (angle > 90) { angle = 180 - angle; n = -n; } if (angle > ALIGN_ANGLE_THRESH) { err_adv(debug, "SWEEP_LINE::setup: slash is not perpendicular to line"); err_adv(debug, " angle: %f", angle); return false; } // compute guideline endpoint: Wpt endpt = o + n.normalized()*a.dist(b); return SWEEP_BASE::setup(_curve->mesh(), o, endpt, dur); }