--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <aversive/pgmspace.h>
+#include <aversive/error.h>
+
+#include <stdint.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <vect_base.h>
+#include <lines.h>
+#include <polygon.h>
+
+#include "img_processing.h"
+
+
+#define debug_printf(fmt, ...) printf_P(PSTR(fmt), ##__VA_ARGS__)
+
+#ifndef HOST_VERSION
+
+#include <aversive.h>
+#include <pwm_ng.h>
+#include <pid.h>
+#include <time.h>
+#include <quadramp.h>
+#include <blocking_detection_manager.h>
+#include <rdline.h>
+
+#include <control_system_manager.h>
+#include <adc.h>
+#include <spi.h>
+#include <ax12.h>
+
+#include "main.h"
+
+#define IMG_DEBUG(args...) DEBUG(E_USER_IMGPROCESS, args)
+#define IMG_NOTICE(args...) NOTICE(E_USER_IMGPROCESS, args)
+#define IMG_ERROR(args...) ERROR(E_USER_IMGPROCESS, args)
+
+#else
+
+#define IMG_DEBUG(args...) debug_printf(args)
+#define IMG_NOTICE(args...) debug_printf(args)
+#define IMG_ERROR(args...) debug_printf(args)
+
+#endif
+
+
+
+
+#define OBJECT_MINIMUM_DEMI_PERIMETER (2*3)
+/* store object in pool if never seen
+ * returns:
+ * 1 if new object
+ * 0 if already known
+ */
+int store_obj(Object_bb* tab_o, int total, Object_bb*o)
+{
+ uint8_t i;
+
+ if (o->x_max - o->x_min + o->y_max - o->y_min < OBJECT_MINIMUM_DEMI_PERIMETER)
+ return 0;
+
+ for (i=0;i<total;i++){
+ if (!memcmp(&tab_o[i], o, sizeof(Object_bb)))
+ return 0;
+
+ if (tab_o[i].x_min==0 && tab_o[i].x_max==0 &&
+ tab_o[i].y_min==0 && tab_o[i].y_max==0){
+ memcpy(&tab_o[i], o, sizeof(Object_bb));
+ return 1;
+ }
+
+ }
+ return 0;
+}
+
+/* step around object of given color and compute its bounding box */
+void object_find_bounding_box(unsigned char* data,
+ int16_t x_in, int16_t y_in,
+ int16_t start_x, int16_t start_y,
+ int16_t color, int16_t color_w,Object_bb* o)
+{
+ int16_t pos_x = start_x;
+ int16_t pos_y = start_y;
+ int16_t start =0;
+ int16_t len = 0;
+ int16_t count_stand = 0;
+
+ vect_t v, vi;
+
+
+ v.x = 1;
+ v.y = 0;
+
+ vi = v;
+
+ o->x_max = 0;
+ o->y_max = 0;
+ o->x_min = x_in;
+ o->y_min = y_in;
+
+
+ while(1){
+ if (pos_x == start_x && pos_y == start_y){
+ count_stand++;
+ if (count_stand>4)
+ break;
+ if( v.x == vi.x && v.y == vi.y && start)
+ break;
+ }
+
+ if (pos_x<o->x_min)
+ o->x_min = pos_x;
+ if (pos_y<o->y_min)
+ o->y_min = pos_y;
+ if (pos_x>o->x_max)
+ o->x_max = pos_x;
+ if (pos_y>o->y_max)
+ o->y_max = pos_y;
+
+ /* is next pixel is good color */
+ if (data[(pos_y+v.y)*x_in + pos_x+v.x] != color){
+ pos_x = pos_x+v.x;
+ pos_y = pos_y+v.y;
+ len++;
+ vect_rot_retro(&v);
+ start = 1;
+ continue;
+ }
+ vect_rot_trigo(&v);
+ }
+
+ o->len = len;
+}
+
+
+
+
+
+/* step around object of given color and computes its polygon */
+void object_find_poly(unsigned char* data,
+ int16_t x_in, int16_t y_in,
+ int16_t start_x, int16_t start_y,
+ int16_t color, int16_t color_w, Object_poly* o)
+{
+ int16_t pos_x = start_x;
+ int16_t pos_y = start_y;
+ int16_t start =0;
+ int16_t len = 0;
+ int16_t count_stand = 0;
+ uint16_t pt_step, pt_num;
+ vect_t v, vi;
+
+ v.x = 1;
+ v.y = 0;
+
+ vi = v;
+
+ pt_step = o->len/POLY_MAX_PTS + 1;
+
+ pt_num = 0;
+
+ while(1){
+ if (pos_x == start_x && pos_y == start_y){
+ count_stand++;
+ if (count_stand>4)
+ break;
+ if( v.x == vi.x && v.y == vi.y && start)
+ break;
+ }
+
+ /* is next pixel is good color */
+ if (data[(pos_y+v.y)*x_in + pos_x+v.x] != color){
+ pos_x = pos_x+v.x;
+ pos_y = pos_y+v.y;
+ len++;
+
+ if (len >pt_num*pt_step){
+ o->pts[pt_num].x = pos_x;
+ o->pts[pt_num].y = pos_y;
+ pt_num+=1;
+ if (pt_num>=POLY_MAX_PTS)
+ break;
+ }
+
+ vect_rot_retro(&v);
+ start = 1;
+ continue;
+ }
+ vect_rot_trigo(&v);
+ }
+
+ o->pts_num = pt_num;
+
+}
+
+#define PT_LEFT 0
+#define PT_RIGHT 2
+#define PT_TOP 1
+#define PT_DOWN 3
+
+/* return most left/right/top/down pts indexes of given polygon */
+void object_find_extrem_points_index(Object_poly* o,
+ unsigned int *pts)
+{
+ unsigned int i;
+ for (i=0;i<4;i++)
+ pts[i] = 0;
+
+
+ for (i=1;i<o->pts_num;i++){
+ if (o->pts[i].x < o->pts[pts[PT_LEFT]].x)
+ pts[PT_LEFT] = i;
+ if (o->pts[i].x > o->pts[pts[PT_RIGHT]].x)
+ pts[PT_RIGHT] = i;
+ if (o->pts[i].y < o->pts[pts[PT_TOP]].y)
+ pts[PT_TOP] = i;
+ if (o->pts[i].y > o->pts[pts[PT_DOWN]].y)
+ pts[PT_DOWN] = i;
+ }
+
+}
+
+
+#define NUM_CALIPERS 4
+/* for debug purpose: display a vector on image */
+void draw_pt_vect(unsigned char *buf, int16_t x_in, int16_t y_in,
+ vect_t *v, point_t p)
+{
+ unsigned int i;
+ float n;
+ int16_t x, y;
+ float coef=1.0;
+
+ if (!v->x && !v->y)
+ return;
+
+ n = vect_norm(v);
+ coef = 1/n;
+ for (i=0;i<5;i++){
+ x = p.x;
+ y = p.y;
+
+ x+=(float)((v->x)*(float)i*(float)coef);
+ y+=(float)((v->y)*(float)i*(float)coef);
+ if ((x== p.x) && (y == p.y))
+ buf[y*x_in+x] = 0x0;
+ else
+ buf[y*x_in+x] = 0x0;
+ }
+
+}
+
+#define CAL_X 3
+#define CAL_Y 0
+
+
+/* compute minimum rectangle area including the given convex polygon */
+void object_poly_get_min_ar(Object_poly *o, unsigned int *pts_index_out,
+ vect_t *v_out, vect_t *r1, vect_t*r2)
+{
+ vect_t calipers[NUM_CALIPERS];
+ vect_t edges[NUM_CALIPERS];
+
+ unsigned int i;
+ unsigned int calipers_pts_index[NUM_CALIPERS];
+ float angles[NUM_CALIPERS];
+ float min_angle;
+ float total_rot_angle = 0;
+ int caliper_result_index;
+
+ vect_t res1, res2;
+ float ps, n1, n2, caliper_n;
+
+ float aera_tmp;
+ /* XXX hack sould be max*/
+ float aera_min=0x100000;
+
+ object_find_extrem_points_index(o, calipers_pts_index);
+
+ calipers[0].x = 0;
+ calipers[0].y = 1;
+
+ calipers[1].x = -1;
+ calipers[1].y = 0;
+
+ calipers[2].x = 0;
+ calipers[2].y = -1;
+
+ calipers[3].x = 1;
+ calipers[3].y = 0;
+
+
+ while (total_rot_angle <= M_PI/2){
+
+ for (i=0;i<NUM_CALIPERS;i++){
+ /* compute polygon edge vector */
+ edges[i].x = o->pts[(calipers_pts_index[i] + 1)%o->pts_num].x -
+ o->pts[calipers_pts_index[i]].x;
+ edges[i].y = o->pts[(calipers_pts_index[i] + 1)%o->pts_num].y -
+ o->pts[calipers_pts_index[i]].y;
+
+ /* compute angle between caliper and polygon edge */
+ angles[i] = vect_get_angle(&edges[i], &calipers[i]);
+ }
+
+ /* find min angle */
+ min_angle = angles[0];
+ caliper_result_index = 0;
+ for (i=1;i<NUM_CALIPERS;i++){
+ if (angles[i]<min_angle){
+ min_angle = angles[i];
+ caliper_result_index = i;
+ }
+ }
+
+ /* rotate calipers */
+ calipers[caliper_result_index] = edges[caliper_result_index];
+
+ for (i=caliper_result_index; i<caliper_result_index + o->pts_num; i++){
+ calipers[(i+1) % NUM_CALIPERS] = calipers[i % NUM_CALIPERS];
+ vect_rot_trigo(&calipers[(i+1) % NUM_CALIPERS]);
+ }
+
+ /* update calipers point */
+ for (i=0;i<NUM_CALIPERS;i++){
+ if (angles[i]==min_angle)
+ calipers_pts_index[i] = (calipers_pts_index[i] + 1) % o->pts_num;
+ }
+
+ res1.x = o->pts[calipers_pts_index[2]].x - o->pts[calipers_pts_index[0]].x;
+ res1.y = o->pts[calipers_pts_index[2]].y - o->pts[calipers_pts_index[0]].y;
+
+ res2.x = o->pts[calipers_pts_index[3]].x - o->pts[calipers_pts_index[1]].x;
+ res2.y = o->pts[calipers_pts_index[3]].y - o->pts[calipers_pts_index[1]].y;
+
+ ps = vect_pscal(&res1, &calipers[CAL_X]);
+ n1 = vect_norm(&res1);
+ caliper_n = vect_norm(&calipers[CAL_X]);
+ caliper_n*=caliper_n;
+
+ res1 = calipers[CAL_X];
+
+ res1.x *= ps/(caliper_n);
+ res1.y *= ps/(caliper_n);
+
+
+ ps = vect_pscal(&res2, &calipers[CAL_Y]);
+ n1 = vect_norm(&res2);
+
+ res2 = calipers[CAL_Y];
+
+ res2.x *= ps/(caliper_n);
+ res2.y *= ps/(caliper_n);
+
+ n1 = vect_norm(&res1);
+ n2 = vect_norm(&res2);
+
+ aera_tmp = n1*n2;
+
+ if (aera_min >aera_tmp){
+ aera_min = aera_tmp;
+ for (i=0;i<NUM_CALIPERS;i++){
+ pts_index_out[i] = calipers_pts_index[i];
+ }
+ *v_out = calipers[0];
+ *r1 = res1;
+ *r2 = res2;
+
+ }
+ total_rot_angle+=min_angle;
+ }
+
+ return;
+}
+
+#define COEF_CALIP 1
+/* transform caliper to rectangle coordinates */
+int object_poly_caliper_to_rectangle(Object_poly *o,
+ unsigned int *pts_index_out, vect_t* caliper,
+ vect_t *r1, vect_t*r2, point_t *p)
+{
+ line_t l1, l2;
+ vect_t caliper_tmp;
+ int ret, i;
+ double mp_x, mp_y;
+ point_t p_int1;//, p_int2;
+
+ point_t p1, p2;
+
+ caliper_tmp = *caliper;
+
+ //IMG_DEBUG("cal: %" PRIi32 " %" PRIi32 "", caliper_tmp.x, caliper_tmp.y);
+
+ mp_x = 0;
+ mp_y = 0;
+
+ /* to be precise, calc 4 intersection of 4 calipers */
+ for (i=0;i<NUM_CALIPERS;i++){
+ p1.x = o->pts[pts_index_out[i]].x;
+ p1.y = o->pts[pts_index_out[i]].y;
+
+ p2.x = p1.x+COEF_CALIP*caliper_tmp.x;
+ p2.y = p1.y+COEF_CALIP*caliper_tmp.y;
+
+ pts2line(&p1, &p2, &l1);
+
+ vect_rot_trigo(&caliper_tmp);
+
+ p1.x = o->pts[pts_index_out[(i+1)%NUM_CALIPERS]].x;
+ p1.y = o->pts[pts_index_out[(i+1)%NUM_CALIPERS]].y;
+
+ p2.x = p1.x+COEF_CALIP*caliper_tmp.x;
+ p2.y = p1.y+COEF_CALIP*caliper_tmp.y;
+
+ pts2line(&p1, &p2, &l2);
+
+ ret = intersect_line(&l1, &l2, &p_int1);
+ if (ret!=1)
+ return 0;
+ //IMG_DEBUG("int1 (%d): %" PRIi32 " %" PRIi32 " ", ret, p_int1.x, p_int1.y);
+
+ mp_x+=p_int1.x;
+ mp_y+=p_int1.y;
+
+ }
+
+
+
+ p->x = lround(mp_x/NUM_CALIPERS);
+ p->y = lround(mp_y/NUM_CALIPERS);
+
+
+ return 1;
+
+}
+
+#define OBJECT_DIM 5
+/* split zone in many column's sized area */
+int split_rectangle(point_t *p, vect_t *r1, vect_t* r2, uint8_t max_zone, zone* zones, uint8_t color)
+{
+ int n1, n2;
+ int i, j;
+ int index=0;
+ int r1_s, r2_s;
+ point_t ptmp;
+
+
+ n1 = vect_norm(r1);
+ n2 = vect_norm(r2);
+
+ r1_s = n1/OBJECT_DIM;
+ r2_s = n2/OBJECT_DIM;
+
+ if (!r1_s || ! r2_s)
+ return 0;
+
+ ptmp.x = p->x - r1->x/2 - r2->x/2 + (r1->x/(r1_s*2))+(r2->x/(r2_s*2));
+ ptmp.y = p->y - r1->y/2 - r2->y/2 + (r1->y/(r1_s*2))+(r2->y/(r2_s*2));
+
+ for(i=0;i<r1_s;i++){
+ for(j=0;j<r2_s;j++){
+ zones[index].p.x = ptmp.x + (i*r1->x)/r1_s+(j*r2->x)/r2_s;
+ zones[index].p.y = ptmp.y + (i*r1->y)/r1_s+(j*r2->y)/r2_s;
+ zones[index].h = color;
+ zones[index].valid = 1;
+
+ index++;
+ if (index>=max_zone)
+ return index;
+
+
+ }
+ }
+
+ return index;
+}
+
+#define OBJECT_SEMI_DIM (OBJECT_DIM/2)
+#define MIN_SURFACE_PERCENT 50
+#define HIGHER_MAX_PIXEL 5
+
+
+int zone_has_enought_pixels(unsigned char* data, int16_t x_in, int16_t y_in, zone* z)
+{
+ int x, y;
+ uint16_t count, total_pix, higher_pixels;
+
+ count = 0;
+ total_pix=0;
+ higher_pixels = 0;
+
+ for (x = -OBJECT_SEMI_DIM;
+ (x <= OBJECT_SEMI_DIM) && (higher_pixels < HIGHER_MAX_PIXEL);
+ x++){
+ for (y = -OBJECT_SEMI_DIM;
+ (y <= OBJECT_SEMI_DIM) && (higher_pixels < HIGHER_MAX_PIXEL);
+ y++){
+ total_pix++;
+ if (data[x_in * (y + z->p.y) + x + z->p.x] == z->h)
+ count++;
+
+ if (data[x_in * (y + z->p.y) + x + z->p.x] > z->h)
+ higher_pixels++;
+
+ }
+
+ }
+
+ IMG_DEBUG("has enougth pixel (h: %d x %"PRIi32": y:%"PRIi32") total: %d/%d (tt: %d, hmax: %d)", z->h, z->p.x, z->p.y,
+ count, (total_pix * MIN_SURFACE_PERCENT) / 100, total_pix, higher_pixels);
+
+ if ((count > (total_pix * MIN_SURFACE_PERCENT) / 100) &&
+ (higher_pixels <HIGHER_MAX_PIXEL))
+ return 1;
+
+ return 0;
+}
+
+int zone_filter_min_surface(unsigned char* data, int16_t x_in, int16_t y_in,
+ uint8_t color, unsigned int zones_num, zone* p)
+{
+ int i;
+
+ for (i = 0; i < zones_num ; i++){
+ if (zone_has_enought_pixels(data, x_in, y_in, &p[i]))
+ continue;
+
+ p[i].valid = 0;
+ }
+
+ IMG_NOTICE("num zone after min surf: %d", zones_num);
+
+ return zones_num;
+
+}
+
+/* center is 15 cm radius*/
+#define CENTER_RADIUS 15
+
+/* the complete column must be in the drop zone*/
+#define CENTER_MAX_DIST (15-3)
+
+/* the column must not be too close from center*/
+#define CENTER_MIN_DIST (8)
+
+int zone_filter_center(unsigned int zones_num, zone* p, int16_t center_x, int16_t center_y, int tweak_min_margin)
+{
+ int i;
+ vect_t v;
+
+ for (i = 0; i < zones_num; i++){
+
+ v.x = p[i].p.x - center_x;
+ v.y = p[i].p.y - center_y;
+ IMG_DEBUG("square dist to center %"PRIi32" (%d %d)",
+ v.x*v.x + v.y*v.y, (CENTER_MIN_DIST+tweak_min_margin) * (CENTER_MIN_DIST+tweak_min_margin), CENTER_MAX_DIST * CENTER_MAX_DIST);
+
+ if (v.x*v.x + v.y*v.y < CENTER_MAX_DIST * CENTER_MAX_DIST &&
+ v.x*v.x + v.y*v.y > (CENTER_MIN_DIST+tweak_min_margin) * (CENTER_MIN_DIST+tweak_min_margin))
+ continue;
+
+ p[i].valid = 0;
+
+ }
+
+ return zones_num;
+}
+
+#define MAX_DIST_TO_ZONE 2
+
+unsigned int zone_filter_zone_rect(unsigned int zones_num, zone* p, int16_t center_x, int16_t center_y , uint8_t working_zone)
+{
+ int i;
+
+ for (i = 0; i < zones_num; i++){
+
+ IMG_DEBUG("rct x:%"PRIi32" y:%"PRIi32" (centerx: %d)",p[i].p.x , p[i].p.y, center_x);
+
+ if ((p[i].p.x > center_x - MAX_DIST_TO_ZONE) && (p[i].h > working_zone))
+ continue;
+
+ p[i].valid = 0;
+ }
+
+ return zones_num;
+}
+
+
+/* delete point to render polygon convex */
+int object_poly_to_convex(Object_poly *o)
+{
+ unsigned int i, j;
+ vect_t v, w;
+ int16_t z;
+ unsigned int del_pts_num = 0;
+
+ for (i=0;i<o->pts_num;){
+ v.x = o->pts[(i + o->pts_num - 1)%o->pts_num].x - o->pts[i].x;
+ v.y = o->pts[(i + o->pts_num - 1)%o->pts_num].y - o->pts[i].y;
+
+ w.x = o->pts[(i+1)%o->pts_num].x - o->pts[i].x;
+ w.y = o->pts[(i+1)%o->pts_num].y - o->pts[i].y;
+
+ z = vect_pvect(&v, &w);
+ if (z>0){
+ i+=1;
+ continue;
+ }
+
+ /* found a convex angle (or colinear points) */
+ for (j = i; j < o->pts_num-1; j++){
+ o->pts[j] = o->pts[j+1];
+ }
+ if (i!=0)
+ i-=1;
+ o->pts_num--;
+ del_pts_num++;
+ }
+
+ return del_pts_num;
+}
+
+
+
+
+
+#define DEFAULT_COLOR 255
+/* scan all image and find objects*/
+unsigned char *parcour_img(unsigned char* data, int16_t x_in, int16_t y_in,
+ Object_bb *sac_obj, Object_poly *sac_obj_poly, int16_t max_obj)
+{
+ int16_t i, obj_num;
+
+ uint8_t in_color=0;
+ int16_t etat;
+
+ Object_bb o;
+ int ret;
+
+ obj_num = 0;
+ /*
+ first, delete borders
+ */
+ for (i=0;i<x_in;i++){
+ data[i] = 0;
+ data[(y_in - 1) * x_in + i] = 0;
+ }
+
+ for (i=0;i<y_in;i++){
+ data[i * x_in] = 0;
+ data[i * x_in + x_in - 1] = 0;
+ }
+
+
+
+ etat = 0;
+ /*
+ 0 look for color (object or edge)
+ 1 look for edge end
+ */
+
+ for (i=1;i<x_in*y_in;i++){
+ switch(etat){
+ case 0:
+ //we are in the dark
+ switch(data[i]){
+ case 0:
+ //look for in dark
+ break;
+ /*
+ case 1:
+ case 2:
+ case 0x15:
+ case 0x24:
+ */
+ default:
+ in_color = data[i];
+ etat = 1;
+ // we found an object
+ object_find_bounding_box(data, x_in, y_in, (i-1)%x_in, (i-1)/x_in, data[i], 255, &o);
+
+ ret = store_obj(sac_obj, max_obj, &o);
+ /* if new object, process rotating calipers */
+ if (ret){
+ sac_obj_poly[obj_num].len = o.len;
+ object_find_poly(data, x_in, y_in,
+ (i-1)%x_in, (i-1)/x_in,
+ data[i], 255, &sac_obj_poly[obj_num]);
+ IMG_DEBUG("%d",sac_obj_poly[obj_num].pts_num);
+ object_poly_to_convex(&sac_obj_poly[obj_num]);
+ /*
+ for (j=0;j<sac_obj_poly[obj_num].pts_num;j++){
+ data[sac_obj_poly[obj_num].pts[j].y*x_in + sac_obj_poly[obj_num].pts[j].x] = 8;
+ }*/
+ sac_obj_poly[obj_num].color = data[i];
+ obj_num++;
+ }
+
+ break;
+
+ case DEFAULT_COLOR:
+ //we must out of color
+ break;
+ }
+ break;
+
+ case 1:
+ if (data[i] != in_color){
+ i--;
+ etat = 0;
+ }
+ /*
+ we are in a color and want to go out of it
+ */
+ break;
+
+ }
+
+
+
+ }
+
+
+ return data;
+
+
+}
+/* space between twin tower is 13 pixels*/
+#define SPACE_INTER_TWIN_TOWER (13)
+
+#define SPACE_INTER_TWIN_TOWER_TOLERENCE 3
+
+
+
+/* find best twin tower for each zone */
+void find_twin_tower(uint8_t zones_num, zone* zones, int8_t sisters[MAX_ZONES][MAX_SISTER_PER_ZONE],
+ int16_t center_x, int16_t center_y)
+{
+
+ uint8_t i, j;
+ uint8_t z_n;
+ int n1, n2;
+ unsigned int z1, z2, z3;
+ //int32_t scal1, scal2;
+ vect_t v, v1, v2;
+ line_t l;
+ point_t p;
+ unsigned int good_zone;
+ double dist, dist2;
+ unsigned int current_sister;
+
+ /* init dummy sisters */
+ for (i = 0; i < zones_num; i++)
+ for (j = 0; j < MAX_SISTER_PER_ZONE; j++)
+ sisters[i][j] = -1;
+
+
+
+ for (z_n = 0; z_n < zones_num; z_n++){
+ if (!zones[z_n].valid)
+ continue;
+
+ current_sister = 0;
+
+ for (i = 0; i < zones_num; i++){
+
+
+ /* we already have max sisters */
+ if (current_sister >= MAX_SISTER_PER_ZONE)
+ break;
+
+
+ if (!zones[i].valid)
+ continue;
+
+
+ if (i == z_n)
+ continue;
+
+ /* twin tower must have same high */
+ if (zones[i].h != zones[z_n].h)
+ continue;
+
+ IMG_DEBUG("test sisters (%"PRIi32" %"PRIi32") (%"PRIi32" %"PRIi32")",
+ zones[z_n].p.x, zones[z_n].p.y,
+ zones[i].p.x, zones[i].p.y);
+
+
+ dist = sqrt( (zones[i].p.x - zones[z_n].p.x) * (zones[i].p.x - zones[z_n].p.x) +
+ (zones[i].p.y - zones[z_n].p.y) * (zones[i].p.y - zones[z_n].p.y) );
+
+
+ IMG_DEBUG("sister space is %2.2f may be near %d", dist, SPACE_INTER_TWIN_TOWER);
+
+ /*
+ twin tower must be close/far enought to drop lintel
+ */
+ if (ABS(dist - SPACE_INTER_TWIN_TOWER) > SPACE_INTER_TWIN_TOWER_TOLERENCE)
+ continue;
+
+
+ pts2line(&zones[i].p, &zones[z_n].p, &l);
+
+ /*
+ test the paralelism of the temple:
+ zone may be on same distance from center
+ */
+
+
+ v1.x = zones[z_n].p.x - center_x;
+ v1.y = zones[z_n].p.y - center_y;
+
+ dist = vect_norm(&v1);
+
+ v2.x = zones[i].p.x - center_x;
+ v2.y = zones[i].p.y - center_y;
+
+ dist2 = vect_norm(&v2);
+
+ IMG_DEBUG("zone dist %2.2f %2.2f", dist, dist2);
+ if (ABS(dist-dist2) > 3){
+ IMG_DEBUG("bad parallelism %2.2f", ABS(dist-dist2));
+ continue;
+ }
+
+
+
+
+ /* no other aligned tower to avoid dropping on a lintel
+ * (3 aligned zone may mean lintel)
+ */
+
+ good_zone = 1;
+
+ for (j = 0; j < zones_num; j++){
+ if (j==i ||j == z_n)
+ continue;
+
+ /* if third zone, but lower */
+ if (zones[j].h <= zones[i].h)
+ continue;
+
+ /*
+ check distance from dropping zone to
+ line formed by twin towers
+ */
+
+ proj_pt_line(&zones[j].p, &l, &p);
+
+
+ /* test if projected point is in the segement */
+
+
+ v.x = zones[z_n].p.x - zones[i].p.x;
+ v.y = zones[z_n].p.y - zones[i].p.y;
+
+ v1.x = p.x - zones[i].p.x;
+ v1.y = p.y - zones[i].p.y;
+
+ n1 = vect_pscal_sign(&v, &v1);
+
+ v.x = -v.x;
+ v.y = -v.y;
+
+ v1.x = p.x - zones[z_n].p.x;
+ v1.y = p.y - zones[z_n].p.y;
+
+
+ n2 =vect_pscal_sign(&v, &v1);
+
+ v.x = p.x - zones[j].p.x;
+ v.y = p.y - zones[j].p.y;
+
+ dist = vect_norm(&v);
+ IMG_DEBUG("dist pt h %d n: (%d %d) (%"PRIi32" %"PRIi32") to line %2.2f", zones[j].h, n1, n2, zones[j].p.x, zones[j].p.y, dist);
+
+
+ if ((n1>=0 && n2>=0) && dist < OBJECT_DIM+2.){
+ good_zone = 0;
+ break;
+ }
+
+
+ /* test if zone is far from points*/
+
+ v1.x = zones[j].p.x - zones[z_n].p.x;
+ v1.y = zones[j].p.y - zones[z_n].p.y;
+
+ dist = vect_norm(&v1);
+ IMG_DEBUG("dist pt to z1 %2.2f", dist);
+
+ if (dist < OBJECT_DIM){
+ good_zone = 0;
+ break;
+ }
+
+ v2.x = zones[j].p.x - zones[i].p.x;
+ v2.y = zones[j].p.y - zones[i].p.y;
+
+
+ dist = vect_norm(&v2);
+ IMG_DEBUG("dist pt to z2 %2.2f", dist);
+
+ if (dist < OBJECT_DIM){
+ good_zone = 0;
+ break;
+ }
+
+
+
+ z1 = i;
+ z2 = z_n;
+ z3 = j;
+
+
+
+
+
+ /*
+ XXX may be a lintel on lintel !!
+ */
+
+ }
+
+ if (!good_zone)
+ continue;
+
+ IMG_DEBUG("sisters ok (%"PRIi32" %"PRIi32") (%"PRIi32" %"PRIi32")",
+ zones[z_n].p.x, zones[z_n].p.y,
+ zones[i].p.x, zones[i].p.y);
+
+
+ sisters[z_n][current_sister] = i;
+ current_sister++;
+ }
+ }
+}
+
+/* test if a higher zone is too close */
+int test_close_zone(uint8_t zones_num, zone* zones, unsigned int z_n)
+{
+ uint8_t i;
+ vect_t v;
+ double dist;
+
+ for (i = 0; i < zones_num; i++){
+ if (i == z_n)
+ continue;
+ if (zones[i].h <= zones[z_n].h)
+ continue;
+
+ v.x = zones[i].p.x - zones[z_n].p.x;
+ v.y = zones[i].p.y - zones[z_n].p.y;
+
+ dist = vect_norm(&v);
+ //IMG_DEBUG("dist pt to pt %2.2f", dist);
+
+ if (dist < OBJECT_DIM){
+ return 1;
+ }
+
+ }
+
+ return 0;
+}
+
+#define MAX_COLUMN 4
+#define MAX_LINTEL 2
+
+drop_column_zone drop_c[MAX_COLUMN];
+drop_lintel_zone drop_l[MAX_LINTEL];
+
+
+void reset_drop_zone(void)
+{
+ memset(drop_c, 0, sizeof(drop_c));
+ memset(drop_l, 0, sizeof(drop_l));
+
+}
+
+
+void display_drop_zones(uint8_t n_columns, uint8_t n_lintels, zone* zones)
+{
+ unsigned int i;
+
+ for (i=0;i<n_columns;i++)
+ IMG_NOTICE("c %d:(h:%d) (%"PRIi32" %"PRIi32") valid=%d",
+ i, drop_c[i].h,
+ zones[drop_c[i].z].p.x, zones[drop_c[i].z].p.y,
+ drop_c[i].valid);
+
+ for (i=0;i<n_lintels;i++)
+ IMG_NOTICE("l %d:(h:%d) (%"PRIi32" %"PRIi32") "
+ "(%"PRIi32" %"PRIi32") valid=%d",
+ i, drop_l[i].h,
+ zones[drop_l[i].z1].p.x, zones[drop_l[i].z1].p.y,
+ zones[drop_l[i].z2].p.x, zones[drop_l[i].z2].p.y, drop_l[i].valid);
+
+}
+
+#define MY_MAX(a, b) ((a)>(b)?(a):(b))
+
+
+#if 0
+#define MAX_DROP_HIGH 8
+
+
+/*
+ recursive function to maximize points during object
+ dropping, given lintel/column number
+ working zone may be 1, 2 or 3
+ */
+unsigned int solve_objects_dropping(unsigned int points, unsigned int points_max,
+ uint8_t n_columns, uint8_t n_lintels,
+ uint8_t zones_num, zone* zones, int8_t sisters[MAX_ZONES][MAX_SISTER_PER_ZONE], uint8_t working_zone)
+{
+
+ uint8_t i, j;
+ unsigned int points_calc;
+ //unsigned int points_added = 0;
+ int sister;
+ int ret;
+
+
+ /* if no more objects, return points */
+ if (n_columns == 0 && n_lintels == 0)
+ return MY_MAX(points, points_max);
+
+ /* start by putting columns if so */
+ for (i = 0; i < zones_num; i++){
+ if (zones[i].h >= MAX_DROP_HIGH)
+ continue;
+
+ if (n_columns){
+
+ ret = test_close_zone(zones_num, zones, i);
+ if (ret)
+ continue;
+
+ zones[i].h++;
+ points_calc = solve_objects_dropping(points + zones[i].h, points_max,
+ n_columns - 1, n_lintels,
+ zones_num, zones, sisters, working_zone);
+
+ if (points_calc > points_max){
+ points_max = points_calc;
+ drop_c[n_columns - 1].z = i;
+ drop_c[n_columns - 1].h = zones[i].h;
+ drop_c[n_columns - 1].valid = 1;
+
+ }
+ zones[i].h--;
+ }
+ /* we must depose all columns before dropping lintels */
+ else if (n_lintels){
+
+ /* dont drop lintel on ground */
+ if (zones[i].h <= working_zone)
+ continue;
+
+ /* XXX todo need get second zone near selected one */
+ //ret = find_twin_tower(zones_num, zones, i, &sister);
+
+ for (j = 0; j < MAX_SISTER_PER_ZONE; j++){
+ sister = sisters[i][j];
+ if (sister == -1)
+ break;
+ if (zones[i].h != zones[sister].h){
+ sister = -1;
+ }
+
+ }
+
+ if (sister == -1)
+ continue;
+ //IMG_DEBUG("sister found: %d %d (h=%d %p)", i, sister, zones[i].h, &zones[i].h);
+
+ zones[i].h++;
+ zones[sister].h++;
+
+
+ points_calc = solve_objects_dropping(points + zones[i].h * 3, points_max,
+ n_columns, n_lintels - 1,
+ zones_num, zones, sisters, working_zone);
+
+ if (points_calc > points_max){
+ points_max = points_calc;
+
+ drop_l[n_lintels - 1].z1 = i;
+ drop_l[n_lintels - 1].z2 = sister;
+ drop_l[n_lintels - 1].h = zones[i].h;
+ drop_l[n_lintels - 1].valid = 1;
+
+ }
+
+
+ zones[sister].h--;
+ zones[i].h--;
+ }
+ }
+
+ return MY_MAX(points, points_max);
+}
+#endif
+
+
+/* */
+int find_column_dropzone(uint8_t zones_num, zone* zones)
+{
+ uint8_t i;
+
+ uint8_t z_n = 0;
+
+ if (zones_num <= 0)
+ return -1;
+
+ for (i = 0; i < zones_num; i++){
+ if (!zones[i].valid)
+ continue;
+ if (zones[i].h > zones[z_n].h)
+ z_n = i;
+ }
+
+
+ /*
+ now, chose dropzone closest to robot
+ meaning little x, big y
+ so maximise y-x
+ */
+ for (i = 0; i < zones_num; i++){
+ if (zones[i].h != zones[z_n].h)
+ continue;
+ if (!zones[i].valid)
+ continue;
+ if (zones[i].p.y - zones[i].p.x > zones[z_n].p.y - zones[z_n].p.x)
+ z_n = i;
+ }
+
+
+
+ return z_n;
+}
+
+
+
+uint8_t color2h(uint8_t color)
+{
+ return (0x100-color)/0x20;
+}
+
+uint8_t h2color(uint8_t color)
+{
+ return color*0x20;
+}
+
+
+#define NUM_ZONE_GENERATE 8
+#define DIST_ZONE_GEN 9
+
+/*
+ remove zone at ground level, and generate zones on
+ a circle at X cm from center
+ */
+unsigned int generate_center_ground_zones(unsigned char* data, int16_t x_in, int16_t y_in,
+ zone * zones, unsigned int zones_num, uint8_t max_zones, int16_t center_x, int16_t center_y)
+{
+ double c_a, s_a;
+ uint8_t i, j;
+ double px1, py1, px2, py2;
+
+
+ /* first del zone at level 2 */
+ for (i = 0; i < zones_num; ){
+ if (zones[i].h!=2){
+ i++;
+ continue;
+ }
+
+ for (j = i; j < zones_num-1; j++)
+ zones[j] = zones[j+1];
+
+ zones_num--;
+
+ }
+
+ /* generate zones around circle */
+
+ c_a = cos(2*M_PI/NUM_ZONE_GENERATE);
+ s_a = sin(2*M_PI/NUM_ZONE_GENERATE);
+
+ px1 = DIST_ZONE_GEN;
+ py1 = 0;
+
+ for (i = 0; i < NUM_ZONE_GENERATE; i++){
+
+ zones[zones_num].p.x = center_x + px1;
+ zones[zones_num].p.y = center_y + py1;
+ zones[zones_num].h = 2;
+ zones[zones_num].valid = 1;
+
+
+ px2 = px1*c_a + py1*s_a;
+ py2 = -px1*s_a + py1*c_a;
+
+ px1 = px2;
+ py1 = py2;
+
+ /* skip zone if it is not in img */
+ if (zones[zones_num].p.x < 0 || zones[zones_num].p.y < 0 ||
+ zones[zones_num].p.x >= x_in || zones[zones_num].p.y > y_in)
+ continue;
+
+ /* skip zone if not enougth pixels */
+ if (!zone_has_enought_pixels(data, x_in, y_in, &zones[zones_num]))
+ continue;
+
+ zones_num++;
+ if (zones_num >= max_zones)
+ break;
+
+ }
+
+ return zones_num;
+
+
+}
+
+
+/*
+ remove zone at ground level, and generate zones on
+ a line at X cm from robot
+ */
+unsigned int generate_rectangle_ground_zones(unsigned char* data, int16_t x_in, int16_t y_in,
+ zone * zones, unsigned int zones_num, uint8_t max_zones, int16_t center_x, int16_t center_y,
+ uint8_t working_zone)
+{
+ uint8_t i, j;
+ uint8_t y;
+
+ /* first del zone at level i */
+ for (i = 0; i < zones_num; ){
+ if (zones[i].h != working_zone ){
+ i++;
+ continue;
+ }
+
+ for (j = i; j < zones_num-1; j++)
+ zones[j] = zones[j+1];
+
+ zones_num--;
+ }
+
+
+ /* generate zones on a line */
+ for (y = OBJECT_DIM; y < y_in; y+=OBJECT_DIM){
+
+ zones[zones_num].p.x = center_x;
+ zones[zones_num].p.y = y;
+ zones[zones_num].h = working_zone ;
+ zones[zones_num].valid = 1;
+
+ if (!zone_has_enought_pixels(data, x_in, y_in, &zones[zones_num]))
+ continue;
+ zones_num++;
+
+ if (zones_num >= max_zones)
+ break;
+
+ }
+ return zones_num;
+
+}
+
+
+#define MAX_DECAL_LINE 5
+#define ENOUGHT_ZONE_PIXEL 2
+
+void recal_img_y(unsigned char* buffer, int16_t x_in, int16_t y_in,
+ uint8_t working_zone)
+{
+ uint8_t i, j;
+ uint8_t cpt;
+
+ /* recal img only for central zone */
+ if (working_zone !=2)
+ return;
+
+ for (i = 0; i < MAX_DECAL_LINE; i++){
+ cpt = 0;
+ for (j = 0; j < x_in; j++){
+ if (buffer[i*x_in + j] ==2)
+ cpt++;
+ }
+
+ if (cpt >= ENOUGHT_ZONE_PIXEL)
+ break;
+ }
+
+ memmove(buffer, &buffer[i * x_in], x_in * y_in - i*x_in);
+ memset(&buffer[x_in * y_in - i * x_in], 0, i*x_in);
+}
+
+
+#define MAX_OBJECTS 20
+
+#define MAX_ZONES_PER_OBJECT 20
+
+
+uint8_t g_zone_num;
+zone g_all_zones[MAX_ZONES];
+
+uint8_t process_img(unsigned char *buffer, int16_t x_in, int16_t y_in,
+ zone * all_zones, uint8_t max_zones)
+{
+
+ int ret;
+ int i, j;
+
+ zone zones[MAX_ZONES_PER_OBJECT];
+
+ vect_t caliper;
+ unsigned int pts_cal[4];
+ point_t ptmp;
+ vect_t r1, r2;
+ int zone_len;
+
+
+ uint8_t zone_num = 0;
+
+ Object_bb sac_obj[MAX_OBJECTS];
+ Object_poly sac_obj_poly[MAX_OBJECTS];
+
+
+
+ /*
+ XXX fix: only decal for working zone 2/(1?)
+ but we dont have info yet
+ */
+ recal_img_y(buffer, x_in, y_in, 2);
+
+ memset(sac_obj, 0, sizeof(sac_obj));
+ memset(sac_obj_poly, 0, sizeof(sac_obj_poly));
+
+
+ /* first, find polygons*/
+ parcour_img(buffer, x_in, y_in, sac_obj, sac_obj_poly, MAX_OBJECTS);
+
+ /* enclose each poygon in the min area polygon
+ then, split each rectangle in dropping zone
+ */
+ for (i=0;i<MAX_OBJECTS;i++){
+
+ if (!sac_obj_poly[i].pts_num)
+ continue;
+
+ IMG_DEBUG("obj: %d %d %d %d %d",
+ i,
+ sac_obj[i].x_min,
+ sac_obj[i].y_min,
+ sac_obj[i].x_max,
+ sac_obj[i].y_max);
+
+ //IMG_DEBUG("poly pts_num: %d", sac_obj_poly[i].pts_num);
+
+ object_poly_get_min_ar(&sac_obj_poly[i], &pts_cal[0], &caliper, &r1, &r2);
+
+ ret = object_poly_caliper_to_rectangle(&sac_obj_poly[i], &pts_cal[0], &caliper,
+ &r1, &r2, &ptmp);
+
+ if (!ret)
+ continue;
+
+ /*
+ IMG_DEBUG("r: (%3"PRIi32" %3"PRIi32") "
+ "(%3"PRIi32" %3"PRIi32")",
+ r1.x, r1.y, r2.x, r2.y);
+ IMG_DEBUG("intersection: %"PRIi32" %"PRIi32"",
+ ptmp.x, ptmp.y);
+ */
+
+ zone_len = split_rectangle(&ptmp, &r1, &r2,
+ MAX_ZONES_PER_OBJECT, &zones[0], sac_obj_poly[i].color);
+ //IMG_DEBUG("split ok %d", zone_len);
+
+ zone_len = zone_filter_min_surface(buffer, x_in, y_in,
+ sac_obj_poly[i].color,
+ zone_len, &zones[0]);
+
+ for (j = 0; j < zone_len && zone_num < max_zones; zone_num++, j++)
+ all_zones[zone_num] = zones[j];
+
+ }
+
+
+ IMG_NOTICE("num zones end: %d", zone_num);
+
+ return zone_num;
+}
+
+
+void process_img_to_zone(unsigned char *buffer, int16_t x_in, int16_t y_in)
+{
+ g_zone_num = process_img(buffer, x_in, y_in,
+ g_all_zones, MAX_ZONES);
+}
+
+uint8_t filter_zones(unsigned char *buffer, int16_t x_in, int16_t y_in,
+ zone * all_zones, uint8_t zone_num, uint8_t max_zones,
+ uint8_t working_zone, int16_t center_x, int16_t center_y,
+ int tweak_min_margin)
+{
+ uint8_t i;
+
+ /* first valid all zones */
+ for (i = 0; i < zone_num; i++){
+ all_zones[i].valid = 1;
+
+ /* filter zone lower thatn working zone */
+ if (all_zones[i].h < working_zone)
+ all_zones[i].valid = 0;
+ }
+
+
+ /*
+ generate correct zone at ground level
+ (depending on working zone)
+ */
+ if (working_zone == 2)
+ zone_num = generate_center_ground_zones(buffer, x_in, y_in,
+ all_zones, zone_num, max_zones, center_x, center_y);
+ else
+ zone_num = generate_rectangle_ground_zones(buffer, x_in, y_in,
+ all_zones, zone_num, max_zones, center_x, center_y,
+ working_zone);
+
+ /* filter zone position, depending on workingzone */
+ if (working_zone == 2)
+ zone_num = zone_filter_center(zone_num, all_zones, center_x, center_y, tweak_min_margin);
+ else
+ zone_num = zone_filter_zone_rect(zone_num, all_zones, center_x, center_y , working_zone);
+
+
+
+ /* display zones (debug purpose) */
+
+ for (i = 0; i < zone_num; i++){
+ //buffer[all_zones[i].p.y*x_in+all_zones[i].p.x] = 0x3;
+ IMG_NOTICE("h:%d (v:%d) x:%"PRIi32" y:%"PRIi32"", all_zones[i].h, all_zones[i].valid, all_zones[i].p.x, all_zones[i].p.y);
+
+ }
+
+ IMG_NOTICE("num zones: %d", zone_num);
+
+
+
+
+ return zone_num;
+}
+
+
+/*
+ return -1 if not column dropzone is found
+ return column hight if found
+*/
+int8_t get_column_dropzone(unsigned char *buffer, int16_t x_in, int16_t y_in,
+ uint8_t working_zone, int16_t center_x, int16_t center_y,
+ int16_t * dropzone_x, int16_t * dropzone_y)
+{
+ uint8_t zone_num;
+ int c_drop_zone;
+
+
+
+ zone_num = filter_zones(buffer, x_in, y_in,
+ g_all_zones, g_zone_num, MAX_ZONES,
+ working_zone, center_x, center_y,
+ 0);
+
+ c_drop_zone = find_column_dropzone(zone_num, g_all_zones);
+
+ if (c_drop_zone<0)
+ return -1;
+
+ *dropzone_x = g_all_zones[c_drop_zone].p.x;
+ *dropzone_y = g_all_zones[c_drop_zone].p.y;
+
+ *dropzone_x = (*dropzone_x) * PIXEL2CM;
+ *dropzone_y = (*dropzone_y) * PIXEL2CM + 30;
+
+ return g_all_zones[c_drop_zone].h;
+}
+
+#define ROBOT_SEMI_INTERCOLUMN_SPACE 75
+
+uint8_t is_temple_there(unsigned char * buffer, int16_t x_in, int16_t y_in,
+ uint8_t h, int16_t center_x, int16_t center_y)
+{
+ zone z;
+ int ret;
+ int i;
+
+
+ IMG_NOTICE("test z (mm) : x:%d y:%d", center_x, center_y);
+ IMG_NOTICE("test z:(pix): x:%d y:%d", (int)(center_x/ PIXEL2CM), (int)(center_y/ PIXEL2CM));
+
+
+ z.p.x = center_x / PIXEL2CM;
+ z.p.y = (center_y - ROBOT_SEMI_INTERCOLUMN_SPACE) / PIXEL2CM ;
+ z.h = h;
+ z.valid = 1;
+ ret = zone_has_enought_pixels(buffer, x_in, y_in, &z);
+ IMG_NOTICE("test z1: %d", ret);
+
+ if (!ret)
+ return 0;
+
+ z.p.x = center_x / PIXEL2CM;
+ z.p.y = (center_y + ROBOT_SEMI_INTERCOLUMN_SPACE)/ PIXEL2CM;
+ z.h = h;
+ z.valid = 1;
+ ret = zone_has_enought_pixels(buffer, x_in, y_in, &z);
+ IMG_NOTICE("test z2: %d", ret);
+ if (!ret)
+ return 0;
+
+
+ /*
+ if middle zone is less or egual to tested temple
+ */
+ z.p.x = center_x / PIXEL2CM;
+ z.p.y = center_y / PIXEL2CM;
+
+ for (i = h; i > 0; i--){
+ z.h = i;
+ z.valid = 1;
+ ret = zone_has_enought_pixels(buffer, x_in, y_in, &z);
+ IMG_NOTICE("test z3:(h=%d) %d", i, ret);
+
+ if (ret)
+ return 1;
+ }
+
+ return 0;
+
+}
+
+
+int8_t find_temple_dropzone(unsigned char *buffer, int16_t x_in, int16_t y_in,
+ uint8_t working_zone, int16_t center_x, int16_t center_y,
+ int16_t * dropzone_x, int16_t * dropzone_y)
+{
+ uint8_t zone_num;
+ int8_t sisters[MAX_ZONES][MAX_SISTER_PER_ZONE];
+ int8_t z_n = -1;
+ uint8_t i, j;
+
+
+ /* find all drop zone */
+ zone_num = filter_zones(buffer, x_in, y_in,
+ g_all_zones, g_zone_num, MAX_ZONES,
+ working_zone, center_x, center_y,
+ -2);
+
+ /* precompute possible twin towers */
+ find_twin_tower(zone_num, g_all_zones, sisters, center_x, center_y);
+
+
+ for (i=0; i< zone_num; i++){
+ IMG_DEBUG("all sisters: %d", i);
+ for (j=0;j<MAX_SISTER_PER_ZONE;j++){
+ IMG_DEBUG("s: %d", sisters[i][j]);
+ }
+ }
+
+ /* only use first sister of each zone */
+ for (i=0; i< zone_num; i++){
+
+ /* if zone doesn't have twin tower */
+ if (sisters[i][0] == -1)
+ continue;
+
+ if (!g_all_zones[i].valid)
+ continue;
+
+ if (z_n == -1){
+ z_n = i;
+ continue;
+ }
+
+ /* if we found higher twin tower */
+ if (g_all_zones[z_n].h > g_all_zones[i].h)
+ continue;
+
+ z_n = i;
+ }
+
+ IMG_NOTICE("twin tower found :z_n=%d", z_n);
+ if (z_n == -1)
+ return -1;
+
+ IMG_NOTICE("(%"PRIi32" %"PRIi32") (%"PRIi32" %"PRIi32")",
+ g_all_zones[z_n].p.x, g_all_zones[z_n].p.y,
+ g_all_zones[sisters[z_n][0]].p.x, g_all_zones[sisters[z_n][0]].p.y);
+
+
+ *dropzone_x = ((double)(g_all_zones[z_n].p.x + g_all_zones[sisters[z_n][0]].p.x)*PIXEL2CM ) / 2;
+ *dropzone_y = ((double)(g_all_zones[z_n].p.y + g_all_zones[sisters[z_n][0]].p.y)*PIXEL2CM ) / 2 + 30;
+
+
+ return g_all_zones[z_n].h;
+
+}
+