static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, &start.dev_vector); if (clockwise) { clockwise = clockwise < 0; /* Join with final face from previous segment */ if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, stroker->contour_tolerance) || ! within_tolerance (&stroker->current_face.cw, &start.cw, stroker->contour_tolerance)) { outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } } } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &start.cw); contour_add_point (stroker, &stroker->ccw, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; }
// Check for equality. // Return number of mismatches greater than epsilon. virtual idx_t compare(const GenericGridBase<T>* ref, T epsilon, int maxPrint = 0, std::ostream& os = std::cerr) const { auto ref1 = dynamic_cast<const GenericGrid1d*>(ref); if (!ref1) { os << "** type mismatch against GenericGrid1d." << endl; return 1; } // Quick check for errors. idx_t errs = count_diffs(*ref, epsilon); // Run detailed comparison if any errors found. if (errs > 0 && maxPrint) { int p = 0; for (idx_t i1 = 0; i1 < get_d1(); i1++) { T te = (*this)(i1); T re = (*ref1)(i1); if (!within_tolerance(te, re, epsilon)) { p++; if (p < maxPrint) os << "** mismatch at (" << i1 << "): " << te << " != " << re << std::endl; else if (p == maxPrint) os << "** Additional errors not printed." << std::endl; else goto done; } } } done: return errs; }
// Check for equality. // Return number of mismatches greater than epsilon. virtual idx_t compare(const GenericGridBase<T>* ref, T epsilon, int maxPrint = 0, std::ostream& os = std::cerr) const { auto ref1 = dynamic_cast<const GenericGrid0d*>(ref); if (!ref1) { os << "** type mismatch against GenericGrid0d." << endl; return 1; } // Quick check for errors. idx_t errs = count_diffs(*ref, epsilon); // Run detailed comparison if any errors found. if (errs > 0 && maxPrint) { T te = (*this)(); T re = (*ref1)(); if (!within_tolerance(te, re, epsilon)) { os << "** scalar mismatch: " << te << " != " << re << std::endl; } } return errs; }
static void contour_add_point (struct stroker *stroker, struct stroke_contour *c, const cairo_point_t *point) { if (! within_tolerance (point, _cairo_contour_last_point (&c->contour), stroker->contour_tolerance)) _cairo_contour_add_point (&c->contour, point); //*_cairo_contour_last_point (&c->contour) = *point; }
// Check for equality. // Return number of mismatches greater than epsilon. virtual idx_t count_diffs(const GenericGridBase<T>& ref, T epsilon) const { idx_t errs = 0; // Count abs diffs > epsilon. #pragma omp parallel for reduction(+:errs) for (idx_t ai = 0; ai < get_num_elems(); ai++) { if (!within_tolerance(_elems[ai], ref._elems[ai], epsilon)) errs++; } return errs; }
static void outer_close (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; int clockwise; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } clockwise = join_is_clockwise (in, out); if (clockwise) { inpt = &in->cw; outpt = &out->cw; outer = &stroker->cw; } else { inpt = &in->ccw; outpt = &out->ccw; outer = &stroker->ccw; } if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) { *_cairo_contour_first_point (&outer->contour) = *_cairo_contour_last_point (&outer->contour); return; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ if ((in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) { add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, clockwise, outer); break; } case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 + in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->dev_slope.x; dy1 = in->dev_slope.y; /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->dev_slope.x; dy2 = out->dev_slope.y; /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); *_cairo_contour_last_point (&outer->contour) = p; *_cairo_contour_first_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } contour_add_point (stroker, outer, outpt); }
static double test_gradients(Crystal *cr, double incr_val, int refine, const char *str, const char *file, PartialityModel pmodel, int quiet, int plot) { Reflection *refl; RefListIterator *iter; long double *vals[3]; int i; int *valid; int nref; int n_good, n_invalid, n_small, n_nan, n_bad; RefList *reflections; FILE *fh = NULL; int ntot = 0; double total = 0.0; char tmp[32]; double *vec1; double *vec2; int n_line; double cc; reflections = find_intersections(crystal_get_image(cr), cr, pmodel); crystal_set_reflections(cr, reflections); nref = num_reflections(reflections); if ( nref < 10 ) { ERROR("Too few reflections found. Failing test by default.\n"); return 0.0; } vals[0] = malloc(nref*sizeof(long double)); vals[1] = malloc(nref*sizeof(long double)); vals[2] = malloc(nref*sizeof(long double)); if ( (vals[0] == NULL) || (vals[1] == NULL) || (vals[2] == NULL) ) { ERROR("Couldn't allocate memory.\n"); return 0.0; } valid = malloc(nref*sizeof(int)); if ( valid == NULL ) { ERROR("Couldn't allocate memory.\n"); return 0.0; } for ( i=0; i<nref; i++ ) valid[i] = 1; scan_partialities(reflections, reflections, valid, vals, 1, pmodel); calc_either_side(cr, incr_val, valid, vals, refine, pmodel); if ( plot ) { snprintf(tmp, 32, "gradient-test-%s.dat", file); fh = fopen(tmp, "w"); } vec1 = malloc(nref*sizeof(double)); vec2 = malloc(nref*sizeof(double)); if ( (vec1 == NULL) || (vec2 == NULL) ) { ERROR("Couldn't allocate memory.\n"); return 0.0; } n_invalid = 0; n_good = 0; n_nan = 0; n_small = 0; n_bad = 0; n_line = 0; i = 0; for ( refl = first_refl(reflections, &iter); refl != NULL; refl = next_refl(refl, iter) ) { long double grad1, grad2, grad; double cgrad; signed int h, k, l; get_indices(refl, &h, &k, &l); if ( !valid[i] ) { n_invalid++; i++; } else { double r1, r2, p; grad1 = (vals[1][i] - vals[0][i]) / incr_val; grad2 = (vals[2][i] - vals[1][i]) / incr_val; grad = (grad1 + grad2) / 2.0; i++; cgrad = p_gradient(cr, refine, refl, pmodel); get_partial(refl, &r1, &r2, &p); if ( isnan(cgrad) ) { n_nan++; continue; } if ( plot ) { fprintf(fh, "%e %Le\n", cgrad, grad); } vec1[n_line] = cgrad; vec2[n_line] = grad; n_line++; if ( (fabs(cgrad) < 5e-8) && (fabs(grad) < 5e-8) ) { n_small++; continue; } total += fabs(cgrad - grad); ntot++; if ( !within_tolerance(grad, cgrad, 5.0) || !within_tolerance(cgrad, grad, 5.0) ) { if ( !quiet ) { STATUS("!- %s %3i %3i %3i" " %10.2Le %10.2e ratio = %5.2Lf" " %10.2e %10.2e\n", str, h, k, l, grad, cgrad, cgrad/grad, r1, r2); } n_bad++; } else { //STATUS("OK %s %3i %3i %3i" // " %10.2Le %10.2e ratio = %5.2Lf" // " %10.2e %10.2e\n", // str, h, k, l, grad, cgrad, cgrad/grad, // r1, r2); n_good++; } } } STATUS("%3s: %3i within 5%%, %3i outside, %3i nan, %3i invalid, " "%3i small. ", str, n_good, n_bad, n_nan, n_invalid, n_small); if ( plot ) { fclose(fh); } cc = gsl_stats_correlation(vec1, 1, vec2, 1, n_line); STATUS("CC = %+f\n", cc); return cc; }