void start_selectregion(GdkEvent *event) { double pt[2]; reset_selection(); ui.cur_item_type = ITEM_SELECTREGION; ui.selection = g_new(struct Selection, 1); ui.selection->type = ITEM_SELECTREGION; ui.selection->items = NULL; ui.selection->layer = ui.cur_layer; get_pointer_coords(event, pt); ui.selection->bbox.left = ui.selection->bbox.right = pt[0]; ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1]; realloc_cur_path(1); ui.cur_path.num_points = 1; ui.cur_path.coords[0] = ui.cur_path.coords[2] = pt[0]; ui.cur_path.coords[1] = ui.cur_path.coords[3] = pt[1]; ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group, gnome_canvas_polygon_get_type(), "width-pixels", 1, "outline-color-rgba", 0x000000ff, "fill-color-rgba", 0x80808040, NULL); make_dashed(ui.selection->canvas_item); update_cursor(); }
void continue_selectregion(GdkEvent *event) { double *pt; realloc_cur_path(ui.cur_path.num_points+1); pt = ui.cur_path.coords + 2*ui.cur_path.num_points; get_pointer_coords(event, pt); if (hypot(pt[0]-pt[-2], pt[1]-pt[-1]) < PIXEL_MOTION_THRESHOLD/ui.zoom) return; // not a meaningful motion ui.cur_path.num_points++; if (ui.cur_path.num_points>2) gnome_canvas_item_set(ui.selection->canvas_item, "points", &ui.cur_path, NULL); }
void make_circle_shape(double x0, double y0, double r) { int npts, i; struct Item *item; struct UndoErasureData *erasure; npts = (int)(2*r); if (npts<12) npts = 12; // min. number of points realloc_cur_path(npts+1); ui.cur_path.num_points = npts+1; for (i=0;i<=npts; i++) { ui.cur_path.coords[2*i] = x0 + r*cos((2*M_PI*i)/npts); ui.cur_path.coords[2*i+1] = y0 + r*sin((2*M_PI*i)/npts); } }
gboolean try_rectangle(void) { struct RecoSegment *rs, *r1, *r2; int i; double dist, avg_angle; // first, we need whole strokes to combine to 4 segments... if (recognizer_queue_length<4) return FALSE; rs = recognizer_queue + recognizer_queue_length - 4; if (rs->startpt!=0) return FALSE; // check edges make angles ~= Pi/2 and vertices roughly match avg_angle = 0.; for (i=0; i<=3; i++) { r1 = rs+i; r2 = rs+(i+1)%4; if (fabs(fabs(r1->angle-r2->angle)-M_PI/2) > RECTANGLE_ANGLE_TOLERANCE) return FALSE; avg_angle += r1->angle; if (r2->angle > r1->angle) avg_angle += (i+1)*M_PI/2; else avg_angle -= (i+1)*M_PI/2; // test if r1 points away from r2 rather than towards it r1->reversed = ((r1->x2-r1->x1)*(r2->xcenter-r1->xcenter)+ (r1->y2-r1->y1)*(r2->ycenter-r1->ycenter)) < 0; } for (i=0; i<=3; i++) { r1 = rs+i; r2 = rs+(i+1)%4; dist = hypot((r1->reversed?r1->x1:r1->x2) - (r2->reversed?r2->x2:r2->x1), (r1->reversed?r1->y1:r1->y2) - (r2->reversed?r2->y2:r2->y1)); if (dist > RECTANGLE_LINEAR_TOLERANCE*(r1->radius+r2->radius)) return FALSE; } // make a rectangle of the correct size and slope avg_angle = avg_angle/4; if (fabs(avg_angle)<SLANT_TOLERANCE) avg_angle = 0.; if (fabs(avg_angle)>M_PI/2-SLANT_TOLERANCE) avg_angle = M_PI/2; realloc_cur_path(5); ui.cur_path.num_points = 5; for (i=0; i<=3; i++) rs[i].angle = avg_angle+i*M_PI/2; for (i=0; i<=3; i++) calc_edge_isect(rs+i, rs+(i+1)%4, ui.cur_path.coords+2*i+2); ui.cur_path.coords[0] = ui.cur_path.coords[8]; ui.cur_path.coords[1] = ui.cur_path.coords[9]; remove_recognized_strokes(rs, 4); insert_recognized_curpath(); return TRUE; }
gboolean try_closed_polygon(int nsides) { struct RecoSegment *rs, *r1, *r2; int i; double dist, pt[2]; // first, we need whole strokes to combine to nsides segments... if (recognizer_queue_length<nsides) return FALSE; rs = recognizer_queue + recognizer_queue_length - nsides; if (rs->startpt!=0) return FALSE; // check vertices roughly match for (i=0; i<nsides; i++) { r1 = rs+i; r2 = rs+(i+1)%nsides; // test if r1 points away from r2 rather than towards it calc_edge_isect(r1, r2, pt); r1->reversed = (hypot(pt[0]-r1->x1,pt[1]-r1->y1) < hypot(pt[0]-r1->x2,pt[1]-r1->y2)); } for (i=0; i<nsides; i++) { r1 = rs+i; r2 = rs+(i+1)%nsides; calc_edge_isect(r1, r2, pt); dist = hypot((r1->reversed?r1->x1:r1->x2)-pt[0],(r1->reversed?r1->y1:r1->y2)-pt[1]) + hypot((r2->reversed?r2->x2:r2->x1)-pt[0],(r2->reversed?r2->y2:r2->y1)-pt[1]); if (dist > POLYGON_LINEAR_TOLERANCE*(r1->radius+r2->radius)) return FALSE; } // make a polygon of the correct size and slope realloc_cur_path(nsides+1); ui.cur_path.num_points = nsides+1; for (i=0; i<nsides; i++) calc_edge_isect(rs+i, rs+(i+1)%nsides, ui.cur_path.coords+2*i+2); ui.cur_path.coords[0] = ui.cur_path.coords[2*nsides]; ui.cur_path.coords[1] = ui.cur_path.coords[2*nsides+1]; remove_recognized_strokes(rs, nsides); insert_recognized_curpath(); return TRUE; }
/* the main pattern recognition function, called after finalize_stroke() */ void recognize_patterns(void) { struct Item *it; struct Inertia s, ss[4]; struct RecoSegment *rs; int n, i; int brk[5]; double score; if (!undo || undo->type!=ITEM_STROKE) return; if (undo->next != last_item_checker) reset_recognizer(); // reset queue if (last_item_checker!=NULL && ui.cur_layer != last_item_checker->layer) reset_recognizer(); it = undo->item; calc_inertia(it->path->coords, 0, it->path->num_points-1, &s); #ifdef RECOGNIZER_DEBUG printf("DEBUG: Mass=%.0f, Center=(%.1f,%.1f), I=(%.0f,%.0f, %.0f), " "Rad=%.2f, Det=%.4f \n", s.mass, center_x(s), center_y(s), I_xx(s), I_yy(s), I_xy(s), I_rad(s), I_det(s)); #endif // first see if it's a polygon n = find_polygonal(it->path->coords, 0, it->path->num_points-1, MAX_POLYGON_SIDES, brk, ss); if (n>0) { optimize_polygonal(it->path->coords, n, brk, ss); #ifdef RECOGNIZER_DEBUG printf("DEBUG: Polygon, %d edges: ", n); for (i=0; i<n; i++) printf("DEBUG: %d-%d (M=%.0f, det=%.4f) ", brk[i], brk[i+1], ss[i].mass, I_det(ss[i])); printf("\n"); #endif /* update recognizer segment queue (most recent at end) */ while (n+recognizer_queue_length > MAX_POLYGON_SIDES) { // remove oldest polygonal stroke i=1; while (i<recognizer_queue_length && recognizer_queue[i].startpt!=0) i++; recognizer_queue_length-=i; g_memmove(recognizer_queue, recognizer_queue+i, recognizer_queue_length * sizeof(struct RecoSegment)); } #ifdef RECOGNIZER_DEBUG printf("DEBUG: Queue now has %d + %d edges\n", recognizer_queue_length, n); #endif rs = recognizer_queue + recognizer_queue_length; recognizer_queue_length += n; for (i=0; i<n; i++) { rs[i].item = it; rs[i].startpt = brk[i]; rs[i].endpt = brk[i+1]; get_segment_geometry(it->path->coords, brk[i], brk[i+1], ss+i, rs+i); } if (try_rectangle()) { reset_recognizer(); return; } if (try_arrow()) { reset_recognizer(); return; } if (try_closed_polygon(3)) { reset_recognizer(); return; } if (try_closed_polygon(4)) { reset_recognizer(); return; } if (n==1) { // current stroke is a line if (fabs(rs->angle)<SLANT_TOLERANCE) { // nearly horizontal rs->angle = 0.; rs->y1 = rs->y2 = rs->ycenter; } if (fabs(rs->angle)>M_PI/2-SLANT_TOLERANCE) { // nearly vertical rs->angle = (rs->angle>0)?(M_PI/2):(-M_PI/2); rs->x1 = rs->x2 = rs->xcenter; } realloc_cur_path(2); ui.cur_path.num_points = 2; ui.cur_path.coords[0] = rs->x1; ui.cur_path.coords[1] = rs->y1; ui.cur_path.coords[2] = rs->x2; ui.cur_path.coords[3] = rs->y2; remove_recognized_strokes(rs, 1); rs->item = insert_recognized_curpath(); } last_item_checker = undo; return; } // not a polygon: maybe a circle ? reset_recognizer(); if (I_det(s)>CIRCLE_MIN_DET) { score = score_circle(it->path->coords, 0, it->path->num_points-1, &s); #ifdef RECOGNIZER_DEBUG printf("DEBUG: Circle score: %.2f\n", score); #endif if (score < CIRCLE_MAX_SCORE) { make_circle_shape(center_x(s), center_y(s), I_rad(s)); recognizer_queue[0].item = it; remove_recognized_strokes(recognizer_queue, 1); insert_recognized_curpath(); } } }
gboolean try_arrow(void) { struct RecoSegment *rs; int i, j; double alpha[3], dist, pt[2], tmp, delta; double x1, y1, x2, y2, angle; gboolean rev[3]; // first, we need whole strokes to combine to nsides segments... if (recognizer_queue_length<3) return FALSE; rs = recognizer_queue + recognizer_queue_length - 3; if (rs->startpt!=0) return FALSE; // check arrow head not too big, and orient main segment for (i=1; i<=2; i++) { if (rs[i].radius > ARROW_MAXSIZE*rs[0].radius) return FALSE; rev[i] = (hypot(rs[i].xcenter-rs->x1, rs[i].ycenter-rs->y1) < hypot(rs[i].xcenter-rs->x2, rs[i].ycenter-rs->y2)); } if (rev[1]!=rev[2]) return FALSE; if (rev[1]) { x1 = rs->x2; y1 = rs->y2; x2 = rs->x1; y2 = rs->y1; angle = rs->angle + M_PI; } else { x1 = rs->x1; y1 = rs->y1; x2 = rs->x2; y2 = rs->y2; angle = rs->angle; } // check arrow head not too big, and angles roughly ok for (i=1; i<=2; i++) { rs[i].reversed = FALSE; alpha[i] = rs[i].angle - angle; while (alpha[i]<-M_PI/2) { alpha[i]+=M_PI; rs[i].reversed = !rs[i].reversed; } while (alpha[i]>M_PI/2) { alpha[i]-=M_PI; rs[i].reversed = !rs[i].reversed; } #ifdef RECOGNIZER_DEBUG printf("DEBUG: arrow: alpha[%d] = %.1f degrees\n", i, alpha[i]*180/M_PI); #endif if (fabs(alpha[i])<ARROW_ANGLE_MIN || fabs(alpha[i])>ARROW_ANGLE_MAX) return FALSE; } // check arrow head segments are roughly symmetric if (alpha[1]*alpha[2]>0 || fabs(alpha[1]+alpha[2]) > ARROW_ASYMMETRY_MAX_ANGLE) return FALSE; if (rs[1].radius/rs[2].radius > 1+ARROW_ASYMMETRY_MAX_LINEAR) return FALSE; if (rs[2].radius/rs[1].radius > 1+ARROW_ASYMMETRY_MAX_LINEAR) return FALSE; // check vertices roughly match calc_edge_isect(rs+1, rs+2, pt); for (j=1; j<=2; j++) { dist = hypot(pt[0]-(rs[j].reversed?rs[j].x1:rs[j].x2), pt[1]-(rs[j].reversed?rs[j].y1:rs[j].y2)); #ifdef RECOGNIZER_DEBUG printf("DEBUG: linear tolerance: tip[%d] = %.2f\n", j, dist/rs[j].radius); #endif if (dist>ARROW_TIP_LINEAR_TOLERANCE*rs[j].radius) return FALSE; } dist = (pt[0]-x2)*sin(angle)-(pt[1]-y2)*cos(angle); dist /= rs[1].radius + rs[2].radius; #ifdef RECOGNIZER_DEBUG printf("DEBUG: sideways gap tolerance = %.2f\n", dist); #endif if (fabs(dist)>ARROW_SIDEWAYS_GAP_TOLERANCE) return FALSE; dist = (pt[0]-x2)*cos(angle)+(pt[1]-y2)*sin(angle); dist /= rs[1].radius + rs[2].radius; #ifdef RECOGNIZER_DEBUG printf("DEBUG: main linear gap = %.2f\n", dist); #endif if (dist<ARROW_MAIN_LINEAR_GAP_MIN || dist>ARROW_MAIN_LINEAR_GAP_MAX) return FALSE; // make an arrow of the correct size and slope if (fabs(rs->angle)<SLANT_TOLERANCE) { // nearly horizontal angle = angle - rs->angle; y1 = y2 = rs->ycenter; } if (rs->angle>M_PI/2-SLANT_TOLERANCE) { // nearly vertical angle = angle - (rs->angle-M_PI/2); x1 = x2 = rs->xcenter; } if (rs->angle<-M_PI/2+SLANT_TOLERANCE) { // nearly vertical angle = angle - (rs->angle+M_PI/2); x1 = x2 = rs->xcenter; } delta = fabs(alpha[1]-alpha[2])/2; dist = (hypot(rs[1].x1-rs[1].x2, rs[1].y1-rs[1].y2) + hypot(rs[2].x1-rs[2].x2, rs[2].y1-rs[2].y2))/2; realloc_cur_path(2); ui.cur_path.num_points = 2; ui.cur_path.coords[0] = x1; ui.cur_path.coords[1] = y1; ui.cur_path.coords[2] = x2; ui.cur_path.coords[3] = y2; remove_recognized_strokes(rs, 3); insert_recognized_curpath(); realloc_cur_path(3); ui.cur_path.num_points = 3; ui.cur_path.coords[0] = x2 - dist*cos(angle+delta); ui.cur_path.coords[1] = y2 - dist*sin(angle+delta); ui.cur_path.coords[2] = x2; ui.cur_path.coords[3] = y2; ui.cur_path.coords[4] = x2 - dist*cos(angle-delta); ui.cur_path.coords[5] = y2 - dist*sin(angle-delta); insert_recognized_curpath(); return TRUE; }