#define PERL_NO_GET_CONTEXT /* we want efficiency */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" /* aesthetic 'node edge repulsion' - makes node and edges spread apart */ #include "aesth.h" typedef struct private { aglo_point e1p, e2p, e1e2, e2e1, e1part, e2part, e, half_force_delta; aglo_point delta; aglo_point force_delta; aglo_real data[1]; } *private; declare_aesth(node_edge_repulsion); define_setup(node_edge_repulsion) { private private; Newc(__LINE__, private, sizeof(struct private) + (10*state->dimensions-1) * sizeof(aglo_real), char, struct private); private->delta = &private->data[0]; private->force_delta = &private->data[state->dimensions]; private->e1p = &private->data[state->dimensions * 2]; private->e2p = &private->data[state->dimensions * 3]; private->e1e2 = &private->data[state->dimensions * 4]; private->e2e1 = &private->data[state->dimensions * 5]; private->e1part = &private->data[state->dimensions * 6]; private->e2part = &private->data[state->dimensions * 7]; private->e = &private->data[state->dimensions * 8]; private->half_force_delta = &private->data[state->dimensions * 9]; return private; } define_cleanup(node_edge_repulsion) { Safefree(private); return; } /* distance from point pn to edge determined by e1n and e2n */ static void ae_point_linesegment_distance_2(aglo_state state, aglo_gradient gradient, void *private, aglo_vertex pn, aglo_vertex e1n, aglo_vertex e2n) { aglo_unsigned d = state->dimensions; aglo_real dp_e2e1p, dp_e1e2p; aglo_point e1p = PRIVATE->e1p; aglo_point e2p = PRIVATE->e2p; aglo_point e1e2 = PRIVATE->e1e2; aglo_point e2e1 = PRIVATE->e2e1; aglo_point_sub(d, e1p, state->point[pn], state->point[e1n]); aglo_point_sub(d, e1e2, state->point[e2n], state->point[e1n]); dp_e2e1p = aglo_point_dot_product(d, e1p, e1e2); if (dp_e2e1p >= 0) { /* acute */ aglo_point_sub(d, e2p, state->point[pn], state->point[e2n]); aglo_point_scalar_mult(d, e2e1, -1, e1e2); dp_e1e2p = aglo_point_dot_product(d, e2p, e2e1); if (dp_e1e2p >= 0) { /* acute */ aglo_point delta = PRIVATE->delta; aglo_point e1part = PRIVATE->e1part; aglo_point e2part = PRIVATE->e2part; aglo_point e = PRIVATE->e; aglo_point force_delta = PRIVATE->force_delta; aglo_point half_force_delta = PRIVATE->half_force_delta; aglo_real mag2; mag2 = aglo_point_mag2(d, e1e2); mag2 = fmax(mag2, 1e-8); /* avoid div by 0 */ aglo_point_scalar_mult(d, e1part, dp_e1e2p/mag2, state->point[e1n]); aglo_point_scalar_mult(d, e2part, dp_e2e1p/mag2, state->point[e2n]); /* e is the point on e1e2 closest to p */ aglo_point_add(d, e, e1part, e2part); aglo_point_sub(d, delta, state->point[pn], e); mag2 = aglo_point_mag2(d, delta); mag2 = fmax(mag2, 1e-8); /* avoid div by 0 */ aglo_point_scalar_mult(d, force_delta, 1/mag2, delta); aglo_point_scalar_mult(d, half_force_delta, 0.5, force_delta); aglo_point_inc(d, &gradient[pn*d], force_delta); aglo_point_dec(d, &gradient[e1n*d], half_force_delta); aglo_point_dec(d, &gradient[e2n*d], half_force_delta); } } /* really should handle the case when not both acute TBF */ } define_aesth(node_edge_repulsion) { aglo_unsigned i, j; aglo_edge_record p; aglo_graph graph = state->graph; /* i < p */ /* j != i; j != p */ for (j=0; jvertices; j++) for (i=0; ivertices; i++) if (i != j) for (p=graph->edge_table[i]; p; p=p->next) if (i < p->tail && j != p->tail) ae_point_linesegment_distance_2(state, gradient, private, j, i, p->tail); } MODULE = Graph::Layout::Aesthetic::Force::NodeEdgeRepulsion PACKAGE = Graph::Layout::Aesthetic::Force::NodeEdgeRepulsion PROTOTYPES: ENABLE SV * new(const char *class) PREINIT: aglo_force force; CODE: New(__LINE__, force, 1, struct aglo_force); force->aesth_gradient = ae_node_edge_repulsion; force->aesth_setup = ae_setup_node_edge_repulsion; force->aesth_cleanup = ae_cleanup_node_edge_repulsion; force->user_data = force->private_data = NULL; RETVAL = NEWSV(1, 0); sv_setref_pv(RETVAL, class, (void*) force); OUTPUT: RETVAL