static void maskrasterize_spline_differentiate_point_outset(float (*diff_feather_points)[2], float (*diff_points)[2], const unsigned int tot_diff_point, const float ofs, const bool do_test) { unsigned int k_prev = tot_diff_point - 2; unsigned int k_curr = tot_diff_point - 1; unsigned int k_next = 0; unsigned int k; float d_prev[2]; float d_next[2]; float d[2]; const float *co_prev; const float *co_curr; const float *co_next; const float ofs_squared = ofs * ofs; co_prev = diff_points[k_prev]; co_curr = diff_points[k_curr]; co_next = diff_points[k_next]; /* precalc */ sub_v2_v2v2(d_prev, co_prev, co_curr); normalize_v2(d_prev); for (k = 0; k < tot_diff_point; k++) { /* co_prev = diff_points[k_prev]; */ /* precalc */ co_curr = diff_points[k_curr]; co_next = diff_points[k_next]; /* sub_v2_v2v2(d_prev, co_prev, co_curr); */ /* precalc */ sub_v2_v2v2(d_next, co_curr, co_next); /* normalize_v2(d_prev); */ /* precalc */ normalize_v2(d_next); if ((do_test == FALSE) || (len_squared_v2v2(diff_feather_points[k], diff_points[k]) < ofs_squared)) { add_v2_v2v2(d, d_prev, d_next); normalize_v2(d); diff_feather_points[k][0] = diff_points[k][0] + ( d[1] * ofs); diff_feather_points[k][1] = diff_points[k][1] + (-d[0] * ofs); } /* use next iter */ copy_v2_v2(d_prev, d_next); /* k_prev = k_curr; */ /* precalc */ k_curr = k_next; k_next++; } }
/* Return the shortest angle in radians between the 2 vectors */ float angle_v2v2(const float v1[2], const float v2[2]) { float vec1[2], vec2[2]; vec1[0] = v1[0]; vec1[1] = v1[1]; vec2[0] = v2[0]; vec2[1] = v2[1]; normalize_v2(vec1); normalize_v2(vec2); return angle_normalized_v2v2(vec1, vec2); }
int BLI_lasso_is_point_inside(int mcords[][2], short moves, const int sx, const int sy, const int error_value) { /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */ float angletot = 0.0, dot, ang, cross, fp1[2], fp2[2]; int a; int *p1, *p2; if (sx == error_value) { return 0; } p1 = mcords[moves - 1]; p2 = mcords[0]; /* first vector */ fp1[0] = (float)(p1[0] - sx); fp1[1] = (float)(p1[1] - sy); normalize_v2(fp1); for (a = 0; a < moves; a++) { /* second vector */ fp2[0] = (float)(p2[0] - sx); fp2[1] = (float)(p2[1] - sy); normalize_v2(fp2); /* dot and angle and cross */ dot = fp1[0] * fp2[0] + fp1[1] * fp2[1]; ang = fabs(saacos(dot)); cross = (float)((p1[1] - p2[1]) * (p1[0] - sx) + (p2[0] - p1[0]) * (p1[1] - sy)); if (cross < 0.0f) angletot -= ang; else angletot += ang; /* circulate */ fp1[0] = fp2[0]; fp1[1] = fp2[1]; p1 = p2; p2 = mcords[a + 1]; } if (fabs(angletot) > 4.0) return 1; return 0; }
void dist_ensure_v2_v2fl(float v1[2], const float v2[2], const float dist) { if (!equals_v2v2(v2, v1)) { float nor[2]; sub_v2_v2v2(nor, v1, v2); normalize_v2(nor); madd_v2_v2v2fl(v1, v2, nor, dist); } }
/** * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)`` */ MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[2]) { float angle_cos; float ab[2]; BLI_ASSERT_UNIT_V2(a); BLI_ASSERT_UNIT_V2(b); add_v2_v2v2(ab, a, b); angle_cos = (normalize_v2(ab) != 0.0f) ? fabsf(dot_v2v2(a, ab)) : 0.0f; return (UNLIKELY(angle_cos < SMALL_NUMBER)) ? 1.0f : (1.0f / angle_cos); }
static GPUTexture * create_jitter_texture(void) { float jitter [64 * 64][2]; int i; for (i = 0; i < 64 * 64; i++) { jitter[i][0] = 2.0f * BLI_frand() - 1.0f; jitter[i][1] = 2.0f * BLI_frand() - 1.0f; normalize_v2(jitter[i]); } return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], NULL); }
static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) { float co[2] = {(float)x, (float)y}; float dir[2], dist; /* move (x,y) vector toward the source by ray_length distance */ sub_v2_v2v2(dir, co, source); dist = normalize_v2(dir); mul_v2_fl(dir, min_ff(dist, ray_length)); sub_v2_v2(co, dir); int ico[2] = {(int)co[0], (int)co[1]}; BLI_rcti_do_minmax_v(rect, ico); }
/** * \return The best angle for fitting the convex hull to an axis aligned bounding box. * * Intended to be used with #BLI_convexhull_2d * * \param points Orded hull points * (result of #BLI_convexhull_2d mapped to a contiguous array). * * \note we could return the index of the best edge too if its needed. */ float BLI_convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], unsigned int n) { unsigned int i, i_prev; float area_best = FLT_MAX; float angle_best = 0.0f; i_prev = n - 1; for (i = 0; i < n; i++) { const float *ev_a = points_hull[i]; const float *ev_b = points_hull[i_prev]; float dvec[2]; sub_v2_v2v2(dvec, ev_a, ev_b); if (normalize_v2(dvec) != 0.0f) { float mat[2][2]; float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX}; unsigned int j; const float angle = atan2f(dvec[0], dvec[1]); float area; angle_to_mat2(mat, angle); for (j = 0; j < n; j++) { float tvec[2]; mul_v2_m2v2(tvec, mat, points_hull[j]); min[0] = min_ff(min[0], tvec[0]); min[1] = min_ff(min[1], tvec[1]); max[0] = max_ff(max[0], tvec[0]); max[1] = max_ff(max[1], tvec[1]); area = (max[0] - min[0]) * (max[1] - min[1]); if (area > area_best) { break; } } if (area < area_best) { area_best = area; angle_best = angle; } } i_prev = i; } return angle_best; }
/** * \return The best angle for fitting the convex hull to an axis aligned bounding box. * * Intended to be used with #BLI_convexhull_2d * * \param points_hull Ordered hull points * (result of #BLI_convexhull_2d mapped to a contiguous array). * * \note we could return the index of the best edge too if its needed. */ float BLI_convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], unsigned int n) { unsigned int i, i_prev; float area_best = FLT_MAX; float dvec_best[2]; /* best angle, delay atan2 */ i_prev = n - 1; for (i = 0; i < n; i++) { const float *ev_a = points_hull[i]; const float *ev_b = points_hull[i_prev]; float dvec[2]; /* 2d rotation matrix */ sub_v2_v2v2(dvec, ev_a, ev_b); if (normalize_v2(dvec) != 0.0f) { /* rotation matrix */ float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX}; unsigned int j; float area; for (j = 0; j < n; j++) { float tvec[2]; mul_v2_v2_cw(tvec, dvec, points_hull[j]); min[0] = min_ff(min[0], tvec[0]); min[1] = min_ff(min[1], tvec[1]); max[0] = max_ff(max[0], tvec[0]); max[1] = max_ff(max[1], tvec[1]); area = (max[0] - min[0]) * (max[1] - min[1]); if (area > area_best) { break; } } if (area < area_best) { area_best = area; copy_v2_v2(dvec_best, dvec); } } i_prev = i; } return (area_best != FLT_MAX) ? atan2f(dvec_best[0], dvec_best[1]) : 0.0f; }
static void getArrowEndPoint(const int width, const int height, const float zoom, const float start_corner[2], const float end_corner[2], float end_point[2]) { float direction[2]; float max_length; sub_v2_v2v2(direction, end_corner, start_corner); direction[0] *= width; direction[1] *= height; max_length = normalize_v2(direction); mul_v2_fl(direction, min_ff(32.0f / zoom, max_length)); direction[0] /= width; direction[1] /= height; add_v2_v2v2(end_point, start_corner, direction); }
float BLI_dial_angle(Dial *dial, float current_position[2]) { float current_direction[2]; sub_v2_v2v2(current_direction, current_position, dial->center); /* only update when we have enough precision, by having the mouse adequately away from center */ if (len_squared_v2(current_direction) > dial->threshold_squared) { float angle; float cosval, sinval; normalize_v2(current_direction); if (!dial->initialized) { copy_v2_v2(dial->initial_direction, current_direction); dial->initialized = true; } /* calculate mouse angle between initial and final mouse position */ cosval = dot_v2v2(current_direction, dial->initial_direction); sinval = cross_v2v2(current_direction, dial->initial_direction); /* clamp to avoid nans in acos */ angle = atan2f(sinval, cosval); /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2 */ if ((angle * dial->last_angle < 0.0f) && (fabsf(dial->last_angle) > (float)M_PI_2)) { if (dial->last_angle < 0.0f) dial->rotations--; else dial->rotations++; } dial->last_angle = angle; return angle + 2.0f * (float)M_PI * dial->rotations; } return dial->last_angle; }
/* draw a given stroke in 2d */ static void gp_draw_stroke (bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, short debug, int offsx, int offsy, int winx, int winy) { /* otherwise thickness is twice that of the 3D view */ float thickness= (float)thickness_s * 0.5f; /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, 'smooth' opengl lines look better * - 'smooth' opengl lines are also required if Image Editor 'image-based' stroke */ if ( (thickness < GP_DRAWTHICKNESS_SPECIAL) || ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) ) { bGPDspoint *pt; int i; glBegin(GL_LINE_STRIP); for (i=0, pt=points; i < totpoints && pt; i++, pt++) { if (sflag & GP_STROKE_2DSPACE) { glVertex2f(pt->x, pt->y); } else if (sflag & GP_STROKE_2DIMAGE) { const float x= (pt->x * winx) + offsx; const float y= (pt->y * winy) + offsy; glVertex2f(x, y); } else { const float x= (pt->x / 100 * winx) + offsx; const float y= (pt->y / 100 * winy) + offsy; glVertex2f(x, y); } } glEnd(); } /* tessellation code - draw stroke as series of connected quads with connection * edges rotated to minimise shrinking artifacts, and rounded endcaps */ else { bGPDspoint *pt1, *pt2; float pm[2]; int i; glShadeModel(GL_FLAT); glBegin(GL_QUADS); for (i=0, pt1=points, pt2=points+1; i < (totpoints-1); i++, pt1++, pt2++) { float s0[2], s1[2]; /* segment 'center' points */ float t0[2], t1[2]; /* tessellated coordinates */ float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ /* get x and y coordinates from points */ if (sflag & GP_STROKE_2DSPACE) { s0[0]= pt1->x; s0[1]= pt1->y; s1[0]= pt2->x; s1[1]= pt2->y; } else if (sflag & GP_STROKE_2DIMAGE) { s0[0]= (pt1->x * winx) + offsx; s0[1]= (pt1->y * winy) + offsy; s1[0]= (pt2->x * winx) + offsx; s1[1]= (pt2->y * winy) + offsy; } else { s0[0]= (pt1->x / 100 * winx) + offsx; s0[1]= (pt1->y / 100 * winy) + offsy; s1[0]= (pt2->x / 100 * winx) + offsx; s1[1]= (pt2->y / 100 * winy) + offsy; } /* calculate gradient and normal - 'angle'=(ny/nx) */ m1[1]= s1[1] - s0[1]; m1[0]= s1[0] - s0[0]; normalize_v2(m1); m2[1]= -m1[0]; m2[0]= m1[1]; /* always use pressure from first point here */ pthick= (pt1->pressure * thickness); /* if the first segment, start of segment is segment's normal */ if (i == 0) { /* draw start cap first * - make points slightly closer to center (about halfway across) */ mt[0]= m2[0] * pthick * 0.5f; mt[1]= m2[1] * pthick * 0.5f; sc[0]= s0[0] - (m1[0] * pthick * 0.75f); sc[1]= s0[1] - (m1[1] * pthick * 0.75f); t0[0]= sc[0] - mt[0]; t0[1]= sc[1] - mt[1]; t1[0]= sc[0] + mt[0]; t1[1]= sc[1] + mt[1]; glVertex2fv(t0); glVertex2fv(t1); /* calculate points for start of segment */ mt[0]= m2[0] * pthick; mt[1]= m2[1] * pthick; t0[0]= s0[0] - mt[0]; t0[1]= s0[1] - mt[1]; t1[0]= s0[0] + mt[0]; t1[1]= s0[1] + mt[1]; /* draw this line twice (first to finish off start cap, then for stroke) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); } /* if not the first segment, use bisector of angle between segments */ else { float mb[2]; /* bisector normal */ float athick, dfac; /* actual thickness, difference between thicknesses */ /* calculate gradient of bisector (as average of normals) */ mb[0]= (pm[0] + m2[0]) / 2; mb[1]= (pm[1] + m2[1]) / 2; normalize_v2(mb); /* calculate gradient to apply * - as basis, use just pthick * bisector gradient * - if cross-section not as thick as it should be, add extra padding to fix it */ mt[0]= mb[0] * pthick; mt[1]= mb[1] * pthick; athick= len_v2(mt); dfac= pthick - (athick * 2); if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick)==0)) { mt[0] += (mb[0] * dfac); mt[1] += (mb[1] * dfac); } /* calculate points for start of segment */ t0[0]= s0[0] - mt[0]; t0[1]= s0[1] - mt[1]; t1[0]= s0[0] + mt[0]; t1[1]= s0[1] + mt[1]; /* draw this line twice (once for end of current segment, and once for start of next) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); } /* if last segment, also draw end of segment (defined as segment's normal) */ if (i == totpoints-2) { /* for once, we use second point's pressure (otherwise it won't be drawn) */ pthick= (pt2->pressure * thickness); /* calculate points for end of segment */ mt[0]= m2[0] * pthick; mt[1]= m2[1] * pthick; t0[0]= s1[0] - mt[0]; t0[1]= s1[1] - mt[1]; t1[0]= s1[0] + mt[0]; t1[1]= s1[1] + mt[1]; /* draw this line twice (once for end of stroke, and once for endcap)*/ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); /* draw end cap as last step * - make points slightly closer to center (about halfway across) */ mt[0]= m2[0] * pthick * 0.5f; mt[1]= m2[1] * pthick * 0.5f; sc[0]= s1[0] + (m1[0] * pthick * 0.75f); sc[1]= s1[1] + (m1[1] * pthick * 0.75f); t0[0]= sc[0] - mt[0]; t0[1]= sc[1] - mt[1]; t1[0]= sc[0] + mt[0]; t1[1]= sc[1] + mt[1]; glVertex2fv(t1); glVertex2fv(t0); } /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } glEnd(); } /* draw debug points of curve on top? (original stroke points) */ if (debug) { bGPDspoint *pt; int i; glBegin(GL_POINTS); for (i=0, pt=points; i < totpoints && pt; i++, pt++) { if (sflag & GP_STROKE_2DSPACE) { glVertex2fv(&pt->x); } else if (sflag & GP_STROKE_2DIMAGE) { const float x= (float)((pt->x * winx) + offsx); const float y= (float)((pt->y * winy) + offsy); glVertex2f(x, y); } else { const float x= (float)(pt->x / 100 * winx) + offsx; const float y= (float)(pt->y / 100 * winy) + offsy; glVertex2f(x, y); } } glEnd(); } }
static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *arg) { Scene *scene = CTX_data_scene(C); UnitSettings *unit = &scene->unit; RulerItem *ruler_item; RulerInfo *ruler_info = arg; RegionView3D *rv3d = ruler_info->ar->regiondata; // ARegion *ar = ruler_info->ar; const float cap_size = 4.0f; const float bg_margin = 4.0f * U.pixelsize; const float bg_radius = 4.0f * U.pixelsize; const float arc_size = 64.0f * U.pixelsize; #define ARC_STEPS 24 const int arc_steps = ARC_STEPS; int i; //unsigned int color_act = 0x666600; unsigned int color_act = 0xffffff; unsigned int color_base = 0x0; unsigned char color_back[4] = {0xff, 0xff, 0xff, 0x80}; unsigned char color_text[3]; unsigned char color_wire[3]; /* anti-aliased lines for more consistent appearance */ glEnable(GL_LINE_SMOOTH); BLF_enable(blf_mono_font, BLF_ROTATION); BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi); BLF_rotation(blf_mono_font, 0.0f); UI_GetThemeColor3ubv(TH_TEXT, color_text); UI_GetThemeColor3ubv(TH_WIRE, color_wire); for (ruler_item = ruler_info->items.first, i = 0; ruler_item; ruler_item = ruler_item->next, i++) { const bool is_act = (i == ruler_info->item_active); float dir_ruler[2]; float co_ss[3][2]; int j; /* should these be checked? - ok for now not to */ for (j = 0; j < 3; j++) { ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP); } glEnable(GL_BLEND); cpack(is_act ? color_act : color_base); if (ruler_item->flag & RULERITEM_USE_ANGLE) { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j++) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); /* arc */ { float dir_tmp[3]; float co_tmp[3]; float arc_ss_coords[ARC_STEPS + 1][2]; float dir_a[3]; float dir_b[3]; float quat[4]; float axis[3]; float angle; const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) * min_fff(arc_size, len_v2v2(co_ss[0], co_ss[1]) / 2.0f, len_v2v2(co_ss[2], co_ss[1]) / 2.0f)); sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]); sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]); normalize_v3(dir_a); normalize_v3(dir_b); cross_v3_v3v3(axis, dir_a, dir_b); angle = angle_normalized_v3v3(dir_a, dir_b); axis_angle_to_quat(quat, axis, angle / arc_steps); copy_v3_v3(dir_tmp, dir_a); glColor3ubv(color_wire); for (j = 0; j <= arc_steps; j++) { madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale); ED_view3d_project_float_global(ar, co_tmp, arc_ss_coords[j], V3D_PROJ_TEST_NOP); mul_qt_v3(quat, dir_tmp); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, arc_ss_coords); glDrawArrays(GL_LINE_STRIP, 0, arc_steps + 1); glDisableClientState(GL_VERTEX_ARRAY); } /* text */ { char numstr[256]; float numstr_size[2]; float pos[2]; const int prec = 2; /* XXX, todo, make optional */ ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); pos[0] = co_ss[1][0] + (cap_size * 2.0f); pos[1] = co_ss[1][1] - (numstr_size[1] / 2.0f); /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_rotation(blf_mono_font, 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec_a[2]; float rot_90_vec_b[2]; float cap[2]; sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]); rot_90_vec_a[0] = -dir_ruler[1]; rot_90_vec_a[1] = dir_ruler[0]; normalize_v2(rot_90_vec_a); sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]); rot_90_vec_b[0] = -dir_ruler[1]; rot_90_vec_b[1] = dir_ruler[0]; normalize_v2(rot_90_vec_b); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size); glVertex2fv(cap); /* angle vertex */ glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] - cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] - cap_size, co_ss[1][1] + cap_size); glVertex2f(co_ss[1][0] + cap_size, co_ss[1][1] - cap_size); glEnd(); glDisable(GL_BLEND); } } else { glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); cpack(0xaaaaaa); setlinestyle(3); glBegin(GL_LINE_STRIP); for (j = 0; j < 3; j += 2) { glVertex2fv(co_ss[j]); } glEnd(); setlinestyle(0); sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]); /* text */ { char numstr[256]; float numstr_size[2]; const int prec = 6; /* XXX, todo, make optional */ float pos[2]; ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec); BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); mid_v2_v2v2(pos, co_ss[0], co_ss[2]); /* center text */ pos[0] -= numstr_size[0] / 2.0f; pos[1] -= numstr_size[1] / 2.0f; /* draw text (bg) */ glColor4ubv(color_back); uiSetRoundBox(UI_CNR_ALL); uiRoundBox(pos[0] - bg_margin, pos[1] - bg_margin, pos[0] + bg_margin + numstr_size[0], pos[1] + bg_margin + numstr_size[1], bg_radius); /* draw text */ glColor3ubv(color_text); BLF_position(blf_mono_font, pos[0], pos[1], 0.0f); BLF_draw(blf_mono_font, numstr, sizeof(numstr)); } /* capping */ { float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]}; float cap[2]; normalize_v2(rot_90_vec); glEnable(GL_BLEND); glColor3ubv(color_wire); glBegin(GL_LINES); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size); glVertex2fv(cap); madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size); glVertex2fv(cap); glEnd(); glDisable(GL_BLEND); } } } glDisable(GL_LINE_SMOOTH); BLF_disable(blf_mono_font, BLF_ROTATION); #undef ARC_STEPS /* draw snap */ if ((ruler_info->snap_flag & RULER_SNAP_OK) && (ruler_info->state == RULER_STATE_DRAG)) { ruler_item = ruler_item_active_get(ruler_info); if (ruler_item) { /* size from drawSnapping */ const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); float co_ss[3]; ED_view3d_project_float_global(ar, ruler_item->co[ruler_item->co_index], co_ss, V3D_PROJ_TEST_NOP); cpack(color_act); circ(co_ss[0], co_ss[1], size * U.pixelsize); } } }
/** only called from #BKE_mask_spline_feather_differentiated_points_with_resolution() ! */ static float (*mask_spline_feather_differentiated_points_with_resolution__double( MaskSpline *spline, unsigned int *tot_feather_point, const unsigned int resol, const bool do_feather_isect))[2] { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); MaskSplinePoint *point_curr, *point_prev; float (*feather)[2], (*fp)[2]; const int tot = BKE_mask_spline_differentiate_calc_total(spline, resol); int a; if (spline->tot_point <= 1) { /* nothing to differentiate */ *tot_feather_point = 0; return NULL; } /* len+1 because of 'forward_diff_bezier' function */ *tot_feather_point = tot; feather = fp = MEM_mallocN((tot + 1) * sizeof(*feather), "mask spline vets"); a = spline->tot_point - 1; if (spline->flag & MASK_SPLINE_CYCLIC) a++; point_prev = points_array; point_curr = point_prev + 1; while (a--) { BezTriple local_prevbezt; BezTriple local_bezt; float point_prev_n[2], point_curr_n[2], tvec[2]; float weight_prev, weight_curr; float len_base, len_feather, len_scalar; BezTriple *bezt_prev; BezTriple *bezt_curr; int j; if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC)) point_curr = points_array; bezt_prev = &point_prev->bezt; bezt_curr = &point_curr->bezt; /* modified copy for feather */ local_prevbezt = *bezt_prev; local_bezt = *bezt_curr; bezt_prev = &local_prevbezt; bezt_curr = &local_bezt; /* calc the normals */ sub_v2_v2v2(tvec, bezt_prev->vec[1], bezt_prev->vec[0]); normalize_v2(tvec); point_prev_n[0] = -tvec[1]; point_prev_n[1] = tvec[0]; sub_v2_v2v2(tvec, bezt_curr->vec[1], bezt_curr->vec[0]); normalize_v2(tvec); point_curr_n[0] = -tvec[1]; point_curr_n[1] = tvec[0]; weight_prev = bezt_prev->weight; weight_curr = bezt_curr->weight; mul_v2_fl(point_prev_n, weight_prev); mul_v2_fl(point_curr_n, weight_curr); /* before we transform verts */ len_base = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]); // add_v2_v2(bezt_prev->vec[0], point_prev_n); // not needed add_v2_v2(bezt_prev->vec[1], point_prev_n); add_v2_v2(bezt_prev->vec[2], point_prev_n); add_v2_v2(bezt_curr->vec[0], point_curr_n); add_v2_v2(bezt_curr->vec[1], point_curr_n); // add_v2_v2(bezt_curr->vec[2], point_curr_n); // not needed len_feather = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]); /* scale by chane in length */ len_scalar = len_feather / len_base; dist_ensure_v2_v2fl(bezt_prev->vec[2], bezt_prev->vec[1], len_scalar * len_v2v2(bezt_prev->vec[2], bezt_prev->vec[1])); dist_ensure_v2_v2fl(bezt_curr->vec[0], bezt_curr->vec[1], len_scalar * len_v2v2(bezt_curr->vec[0], bezt_curr->vec[1])); for (j = 0; j < 2; j++) { BKE_curve_forward_diff_bezier(bezt_prev->vec[1][j], bezt_prev->vec[2][j], bezt_curr->vec[0][j], bezt_curr->vec[1][j], &(*fp)[j], resol, 2 * sizeof(float)); } /* scale by the uw's */ if (point_prev->tot_uw) { for (j = 0; j < resol; j++, fp++) { float u = (float) j / resol; float weight_uw, weight_scalar; float co[2]; /* TODO - these calls all calculate similar things * could be unified for some speed */ BKE_mask_point_segment_co(spline, point_prev, u, co); weight_uw = BKE_mask_point_weight(spline, point_prev, u); weight_scalar = BKE_mask_point_weight_scalar(spline, point_prev, u); dist_ensure_v2_v2fl(*fp, co, len_v2v2(*fp, co) * (weight_uw / weight_scalar)); } } else { fp += resol; } if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC) == 0) { copy_v2_v2(*fp, bezt_curr->vec[1]); } point_prev = point_curr; point_curr++; } if ((spline->flag & MASK_SPLINE_NOINTERSECT) && do_feather_isect) { BKE_mask_spline_feather_collapse_inner_loops(spline, feather, tot); } return feather; }
/* draw a given stroke in 2d */ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, bool debug, int offsx, int offsy, int winx, int winy) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; /* strokes in Image Editor need a scale factor, since units there are not pixels! */ float scalefac = 1.0f; if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { scalefac = 0.001f; } /* tessellation code - draw stroke as series of connected quads with connection * edges rotated to minimize shrinking artifacts, and rounded endcaps */ { bGPDspoint *pt1, *pt2; float pm[2]; int i; glShadeModel(GL_FLAT); glBegin(GL_QUADS); for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { float s0[2], s1[2]; /* segment 'center' points */ float t0[2], t1[2]; /* tessellated coordinates */ float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ /* get x and y coordinates from points */ gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0); gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1); /* calculate gradient and normal - 'angle'=(ny/nx) */ m1[1] = s1[1] - s0[1]; m1[0] = s1[0] - s0[0]; normalize_v2(m1); m2[1] = -m1[0]; m2[0] = m1[1]; /* always use pressure from first point here */ pthick = (pt1->pressure * thickness * scalefac); /* if the first segment, start of segment is segment's normal */ if (i == 0) { /* draw start cap first * - make points slightly closer to center (about halfway across) */ mt[0] = m2[0] * pthick * 0.5f; mt[1] = m2[1] * pthick * 0.5f; sc[0] = s0[0] - (m1[0] * pthick * 0.75f); sc[1] = s0[1] - (m1[1] * pthick * 0.75f); t0[0] = sc[0] - mt[0]; t0[1] = sc[1] - mt[1]; t1[0] = sc[0] + mt[0]; t1[1] = sc[1] + mt[1]; glVertex2fv(t0); glVertex2fv(t1); /* calculate points for start of segment */ mt[0] = m2[0] * pthick; mt[1] = m2[1] * pthick; t0[0] = s0[0] - mt[0]; t0[1] = s0[1] - mt[1]; t1[0] = s0[0] + mt[0]; t1[1] = s0[1] + mt[1]; /* draw this line twice (first to finish off start cap, then for stroke) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); } /* if not the first segment, use bisector of angle between segments */ else { float mb[2]; /* bisector normal */ float athick, dfac; /* actual thickness, difference between thicknesses */ /* calculate gradient of bisector (as average of normals) */ mb[0] = (pm[0] + m2[0]) / 2; mb[1] = (pm[1] + m2[1]) / 2; normalize_v2(mb); /* calculate gradient to apply * - as basis, use just pthick * bisector gradient * - if cross-section not as thick as it should be, add extra padding to fix it */ mt[0] = mb[0] * pthick; mt[1] = mb[1] * pthick; athick = len_v2(mt); dfac = pthick - (athick * 2); if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { mt[0] += (mb[0] * dfac); mt[1] += (mb[1] * dfac); } /* calculate points for start of segment */ t0[0] = s0[0] - mt[0]; t0[1] = s0[1] - mt[1]; t1[0] = s0[0] + mt[0]; t1[1] = s0[1] + mt[1]; /* draw this line twice (once for end of current segment, and once for start of next) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); } /* if last segment, also draw end of segment (defined as segment's normal) */ if (i == totpoints - 2) { /* for once, we use second point's pressure (otherwise it won't be drawn) */ pthick = (pt2->pressure * thickness * scalefac); /* calculate points for end of segment */ mt[0] = m2[0] * pthick; mt[1] = m2[1] * pthick; t0[0] = s1[0] - mt[0]; t0[1] = s1[1] - mt[1]; t1[0] = s1[0] + mt[0]; t1[1] = s1[1] + mt[1]; /* draw this line twice (once for end of stroke, and once for endcap)*/ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(t1); /* draw end cap as last step * - make points slightly closer to center (about halfway across) */ mt[0] = m2[0] * pthick * 0.5f; mt[1] = m2[1] * pthick * 0.5f; sc[0] = s1[0] + (m1[0] * pthick * 0.75f); sc[1] = s1[1] + (m1[1] * pthick * 0.75f); t0[0] = sc[0] - mt[0]; t0[1] = sc[1] - mt[1]; t1[0] = sc[0] + mt[0]; t1[1] = sc[1] + mt[1]; glVertex2fv(t1); glVertex2fv(t0); } /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } glEnd(); } /* draw debug points of curve on top? (original stroke points) */ if (debug) { bGPDspoint *pt; int i; glPointSize((float)(thickness_s + 2)); glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); glVertex2fv(co); } glEnd(); } }
int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float pos[2], double time, float pressure, void *user, int use_color_correction) { Scene *scene = painter->scene; Brush *brush = painter->brush; int totpaintops = 0; if (pressure == 0.0f) { if (painter->lastpressure) // XXX - hack, operator misses pressure = painter->lastpressure; else pressure = 1.0f; /* zero pressure == not using tablet */ } if (painter->firsttouch) { /* paint exactly once on first touch */ painter->startpaintpos[0] = pos[0]; painter->startpaintpos[1] = pos[1]; brush_pressure_apply(painter, brush, pressure); if (painter->cache.enabled) brush_painter_refresh_cache(painter, pos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, pos); painter->lasttime = time; painter->firsttouch = 0; painter->lastpaintpos[0] = pos[0]; painter->lastpaintpos[1] = pos[1]; } #if 0 else if (painter->brush->flag & BRUSH_AIRBRUSH) { float spacing, step, paintpos[2], dmousepos[2], len; double starttime, curtime = time; /* compute brush spacing adapted to brush size */ spacing = brush->rate; //radius*brush->spacing * 0.01f; /* setup starting time, direction vector and accumulated time */ starttime = painter->accumtime; sub_v2_v2v2(dmousepos, pos, painter->lastmousepos); len = normalize_v2(dmousepos); painter->accumtime += curtime - painter->lasttime; /* do paint op over unpainted time distance */ while (painter->accumtime >= spacing) { step = (spacing - starttime) * len; paintpos[0] = painter->lastmousepos[0] + dmousepos[0] * step; paintpos[1] = painter->lastmousepos[1] + dmousepos[1] * step; if (painter->cache.enabled) brush_painter_refresh_cache(painter); totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); painter->lastpaintpos[0] = paintpos[0]; painter->lastpaintpos[1] = paintpos[1]; painter->accumtime -= spacing; starttime -= spacing; } painter->lasttime = curtime; } #endif else { float startdistance, spacing, step, paintpos[2], dmousepos[2], finalpos[2]; float t, len, press; const int radius = BKE_brush_size_get(scene, brush); /* compute brush spacing adapted to brush radius, spacing may depend * on pressure, so update it */ brush_pressure_apply(painter, brush, painter->lastpressure); spacing = max_ff(1.0f, radius) * brush->spacing * 0.01f; /* setup starting distance, direction vector and accumulated distance */ startdistance = painter->accumdistance; sub_v2_v2v2(dmousepos, pos, painter->lastmousepos); len = normalize_v2(dmousepos); painter->accumdistance += len; if (brush->flag & BRUSH_SPACE) { /* do paint op over unpainted distance */ while ((len > 0.0f) && (painter->accumdistance >= spacing)) { step = spacing - startdistance; paintpos[0] = painter->lastmousepos[0] + dmousepos[0] * step; paintpos[1] = painter->lastmousepos[1] + dmousepos[1] * step; t = step / len; press = (1.0f - t) * painter->lastpressure + t * pressure; brush_pressure_apply(painter, brush, press); spacing = max_ff(1.0f, radius) * brush->spacing * 0.01f; BKE_brush_jitter_pos(scene, brush, paintpos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, finalpos); painter->lastpaintpos[0] = paintpos[0]; painter->lastpaintpos[1] = paintpos[1]; painter->accumdistance -= spacing; startdistance -= spacing; } } else { BKE_brush_jitter_pos(scene, brush, pos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, finalpos); painter->lastpaintpos[0] = pos[0]; painter->lastpaintpos[1] = pos[1]; painter->accumdistance = 0; } /* do airbrush paint ops, based on the number of paint ops left over * from regular painting. this is a temporary solution until we have * accurate time stamps for mouse move events */ if (brush->flag & BRUSH_AIRBRUSH) { double curtime = time; double painttime = brush->rate * totpaintops; painter->accumtime += curtime - painter->lasttime; if (painter->accumtime <= painttime) painter->accumtime = 0.0; else painter->accumtime -= painttime; while (painter->accumtime >= (double)brush->rate) { brush_pressure_apply(painter, brush, pressure); BKE_brush_jitter_pos(scene, brush, pos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastmousepos, finalpos); painter->accumtime -= (double)brush->rate; } painter->lasttime = curtime; } } painter->lastmousepos[0] = pos[0]; painter->lastmousepos[1] = pos[1]; painter->lastpressure = pressure; BKE_brush_alpha_set(scene, brush, painter->startalpha); BKE_brush_size_set(scene, brush, painter->startsize); brush->jitter = painter->startjitter; brush->spacing = painter->startspacing; return totpaintops; }
/* tries to realize the wanted velocity taking all constraints into account */ void boid_body(BoidBrainData *bbd, ParticleData *pa) { BoidSettings *boids = bbd->part->boids; BoidParticle *bpa = pa->boid; BoidValues val; EffectedPoint epoint; float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3]; float dvec[3], bvec[3]; float new_dir[3], new_speed; float old_dir[3], old_speed; float wanted_dir[3]; float q[4], mat[3][3]; /* rotation */ float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f}; float force[3] = {0.0f, 0.0f, 0.0f}; float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; set_boid_values(&val, boids, pa); /* make sure there's something in new velocity, location & rotation */ copy_particle_key(&pa->state, &pa->prev_state, 0); if (bbd->part->flag & PART_SIZEMASS) pa_mass*=pa->size; /* if boids can't fly they fall to the ground */ if ((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim)) bpa->data.mode = eBoidMode_Falling; if (bpa->data.mode == eBoidMode_Falling) { /* Falling boids are only effected by gravity. */ acc[2] = bbd->sim->scene->physics_settings.gravity[2]; } else { /* figure out acceleration */ float landing_level = 2.0f; float level = landing_level + 1.0f; float new_vel[3]; if (bpa->data.mode == eBoidMode_Liftoff) { bpa->data.mode = eBoidMode_InAir; bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); } else if (bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { /* auto-leveling & landing if close to ground */ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* level = how many particle sizes above ground */ level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f; landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass; if (pa->prev_state.vel[2] < 0.0f) { if (level < 1.0f) { bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f; bbd->wanted_speed = 0.0f; bpa->data.mode = eBoidMode_Falling; } else if (level < landing_level) { bbd->wanted_speed *= (level - 1.0f)/landing_level; bbd->wanted_co[2] *= (level - 1.0f)/landing_level; } } } copy_v3_v3(old_dir, pa->prev_state.ave); new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co); /* first check if we have valid direction we want to go towards */ if (new_speed == 0.0f) { copy_v3_v3(new_dir, old_dir); } else { float old_dir2[2], wanted_dir2[2], nor[3], angle; copy_v2_v2(old_dir2, old_dir); normalize_v2(old_dir2); copy_v2_v2(wanted_dir2, wanted_dir); normalize_v2(wanted_dir2); /* choose random direction to turn if wanted velocity */ /* is directly behind regardless of z-coordinate */ if (dot_v2v2(old_dir2, wanted_dir2) < -0.99f) { wanted_dir[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); wanted_dir[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); wanted_dir[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); normalize_v3(wanted_dir); } /* constrain direction with maximum angular velocity */ angle = saacos(dot_v3v3(old_dir, wanted_dir)); angle = min_ff(angle, val.max_ave); cross_v3_v3v3(nor, old_dir, wanted_dir); axis_angle_to_quat(q, nor, angle); copy_v3_v3(new_dir, old_dir); mul_qt_v3(q, new_dir); normalize_v3(new_dir); /* save direction in case resulting velocity too small */ axis_angle_to_quat(q, nor, angle*dtime); copy_v3_v3(pa->state.ave, old_dir); mul_qt_v3(q, pa->state.ave); normalize_v3(pa->state.ave); } /* constrain speed with maximum acceleration */ old_speed = len_v3(pa->prev_state.vel); if (bbd->wanted_speed < old_speed) new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc); else new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc); /* combine direction and speed */ copy_v3_v3(new_vel, new_dir); mul_v3_fl(new_vel, new_speed); /* maintain minimum flying velocity if not landing */ if (level >= landing_level) { float len2 = dot_v2v2(new_vel, new_vel); float root; len2 = MAX2(len2, val.min_speed*val.min_speed); root = sasqrt(new_speed*new_speed - len2); new_vel[2] = new_vel[2] < 0.0f ? -root : root; normalize_v2(new_vel); mul_v2_fl(new_vel, sasqrt(len2)); } /* finally constrain speed to max speed */ new_speed = normalize_v3(new_vel); mul_v3_fl(new_vel, MIN2(new_speed, val.max_speed)); /* get acceleration from difference of velocities */ sub_v3_v3v3(acc, new_vel, pa->prev_state.vel); /* break acceleration to components */ project_v3_v3v3(tan_acc, acc, pa->prev_state.ave); sub_v3_v3v3(nor_acc, acc, tan_acc); } /* account for effectors */ pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint); pdDoEffectors(bbd->sim->psys->effectors, bbd->sim->colliders, bbd->part->effector_weights, &epoint, force, NULL); if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) { float length = normalize_v3(force); length = MAX2(0.0f, length - boids->land_stick_force); mul_v3_fl(force, length); } add_v3_v3(acc, force); /* store smoothed acceleration for nice banking etc. */ madd_v3_v3fl(bpa->data.acc, acc, dtime); mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime)); /* integrate new location & velocity */ /* by regarding the acceleration as a force at this stage we*/ /* can get better control allthough it's a bit unphysical */ mul_v3_fl(acc, 1.0f/pa_mass); copy_v3_v3(dvec, acc); mul_v3_fl(dvec, dtime*dtime*0.5f); copy_v3_v3(bvec, pa->prev_state.vel); mul_v3_fl(bvec, dtime); add_v3_v3(dvec, bvec); add_v3_v3(pa->state.co, dvec); madd_v3_v3fl(pa->state.vel, acc, dtime); //if (bpa->data.mode != eBoidMode_InAir) bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* change modes, constrain movement & keep track of down vector */ switch (bpa->data.mode) { case eBoidMode_InAir: { float grav[3]; grav[0] = 0.0f; grav[1] = 0.0f; grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* don't take forward acceleration into account (better banking) */ if (dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) { project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); } else { copy_v3_v3(dvec, bpa->data.acc); } /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } else if (pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { /* land boid when below ground */ if (boids->options & BOID_ALLOW_LAND) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* fly above ground */ else if (bpa->ground) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } } break; } case eBoidMode_Falling: { float grav[3]; grav[0] = 0.0f; grav[1] = 0.0f; grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* gather apparent gravity */ madd_v3_v3fl(bpa->gravity, grav, dtime); normalize_v3(bpa->gravity); if (boids->options & BOID_ALLOW_LAND) { /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* land boid when really near ground */ else if (pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* if we're falling, can fly and want to go upwards lets fly */ else if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) bpa->data.mode = eBoidMode_InAir; } else bpa->data.mode = eBoidMode_InAir; break; } case eBoidMode_Climbing: { boid_climb(boids, pa, ground_co, ground_nor); //float nor[3]; //copy_v3_v3(nor, ground_nor); ///* gather apparent gravity to r_ve */ //madd_v3_v3fl(pa->r_ve, ground_nor, -1.0); //normalize_v3(pa->r_ve); ///* raise boid it's size from surface */ //mul_v3_fl(nor, pa->size * boids->height); //add_v3_v3v3(pa->state.co, ground_co, nor); ///* remove normal component from velocity */ //project_v3_v3v3(v, pa->state.vel, ground_nor); //sub_v3_v3v3(pa->state.vel, pa->state.vel, v); break; } case eBoidMode_OnLand: { /* stick boid on goal when close enough */ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* ground is too far away so boid falls */ else if (pa->state.co[2]-ground_co[2] > 1.1f * pa->size * boids->height) bpa->data.mode = eBoidMode_Falling; else { /* constrain to surface */ pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } if (boids->banking > 0.0f) { float grav[3]; /* Don't take gravity's strength in to account, */ /* otherwise amount of banking is hard to control. */ negate_v3_v3(grav, ground_nor); project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); } else { /* gather negative surface normal */ madd_v3_v3fl(bpa->gravity, ground_nor, -1.0f); normalize_v3(bpa->gravity); } break; } } /* save direction to state.ave unless the boid is falling */ /* (boids can't effect their direction when falling) */ if (bpa->data.mode!=eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f*pa->size) { copy_v3_v3(pa->state.ave, pa->state.vel); pa->state.ave[2] *= bbd->part->boids->pitch; normalize_v3(pa->state.ave); } /* apply damping */ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) mul_v3_fl(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); /* calculate rotation matrix based on forward & down vectors */ if (bpa->data.mode == eBoidMode_InAir) { copy_v3_v3(mat[0], pa->state.ave); project_v3_v3v3(dvec, bpa->gravity, pa->state.ave); sub_v3_v3v3(mat[2], bpa->gravity, dvec); normalize_v3(mat[2]); } else { project_v3_v3v3(dvec, pa->state.ave, bpa->gravity); sub_v3_v3v3(mat[0], pa->state.ave, dvec); normalize_v3(mat[0]); copy_v3_v3(mat[2], bpa->gravity); } negate_v3(mat[2]); cross_v3_v3v3(mat[1], mat[2], mat[0]); /* apply rotation */ mat3_to_quat_is_ok(q, mat); copy_qt_qt(pa->state.rot, q); }
/* determines the velocity the boid wants to have */ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) { BoidRule *rule; BoidSettings *boids = bbd->part->boids; BoidValues val; BoidState *state = get_boid_state(boids, pa); BoidParticle *bpa = pa->boid; ParticleSystem *psys = bbd->sim->psys; int rand; //BoidCondition *cond; if (bpa->data.health <= 0.0f) { pa->alive = PARS_DYING; pa->dietime = bbd->cfra; return; } //planned for near future //cond = state->conditions.first; //for (; cond; cond=cond->next) { // if (boid_condition_is_true(cond)) { // pa->boid->state_id = cond->state_id; // state = get_boid_state(boids, pa); // break; /* only first true condition is used */ // } //} zero_v3(bbd->wanted_co); bbd->wanted_speed = 0.0f; /* create random seed for every particle & frame */ rand = (int)(psys_frand(psys, psys->seed + p) * 1000); rand = (int)(psys_frand(psys, (int)bbd->cfra + rand) * 1000); set_boid_values(&val, bbd->part->boids, pa); /* go through rules */ switch (state->ruleset_type) { case eBoidRulesetType_Fuzzy: { for (rule = state->rules.first; rule; rule = rule->next) { if (apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness)) break; /* only first nonzero rule that comes through fuzzy rule is applied */ } break; } case eBoidRulesetType_Random: { /* use random rule for each particle (always same for same particle though) */ rule = BLI_findlink(&state->rules, rand % BLI_listbase_count(&state->rules)); apply_boid_rule(bbd, rule, &val, pa, -1.0); break; } case eBoidRulesetType_Average: { float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f; int n = 0; for (rule = state->rules.first; rule; rule=rule->next) { if (apply_boid_rule(bbd, rule, &val, pa, -1.0f)) { add_v3_v3(wanted_co, bbd->wanted_co); wanted_speed += bbd->wanted_speed; n++; zero_v3(bbd->wanted_co); bbd->wanted_speed = 0.0f; } } if (n > 1) { mul_v3_fl(wanted_co, 1.0f/(float)n); wanted_speed /= (float)n; } copy_v3_v3(bbd->wanted_co, wanted_co); bbd->wanted_speed = wanted_speed; break; } } /* decide on jumping & liftoff */ if (bpa->data.mode == eBoidMode_OnLand) { /* fuzziness makes boids capable of misjudgement */ float mul = 1.0f + state->rule_fuzziness; if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) { float cvel[3], dir[3]; copy_v3_v3(dir, pa->prev_state.ave); normalize_v2(dir); copy_v3_v3(cvel, bbd->wanted_co); normalize_v2(cvel); if (dot_v2v2(cvel, dir) > 0.95f / mul) bpa->data.mode = eBoidMode_Liftoff; } else if (val.jump_speed > 0.0f) { float jump_v[3]; int jump = 0; /* jump to get to a location */ if (bbd->wanted_co[2] > 0.0f) { float cvel[3], dir[3]; float z_v, ground_v, cur_v; float len; copy_v3_v3(dir, pa->prev_state.ave); normalize_v2(dir); copy_v3_v3(cvel, bbd->wanted_co); normalize_v2(cvel); len = len_v2(pa->prev_state.vel); /* first of all, are we going in a suitable direction? */ /* or at a suitably slow speed */ if (dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) { /* try to reach goal at highest point of the parabolic path */ cur_v = len_v2(pa->prev_state.vel); z_v = sasqrt(-2.0f * bbd->sim->scene->physics_settings.gravity[2] * bbd->wanted_co[2]); ground_v = len_v2(bbd->wanted_co)*sasqrt(-0.5f * bbd->sim->scene->physics_settings.gravity[2] / bbd->wanted_co[2]); len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v); if (len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) { jump = 1; len = MIN2(len, val.jump_speed); copy_v3_v3(jump_v, dir); jump_v[2] = z_v; mul_v3_fl(jump_v, ground_v); normalize_v3(jump_v); mul_v3_fl(jump_v, len); add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel); } } } /* jump to go faster */ if (jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) { } if (jump) { copy_v3_v3(pa->prev_state.vel, jump_v); bpa->data.mode = eBoidMode_Falling; } } } }