/** * cpml_segment_offset: * @segment: a #CpmlSegment * @offset: the offset distance * * Offsets a segment of the specified amount, that is builds a "parallel" * segment at the @offset distance from the original one and returns the * result by replacing the original @segment. * * <important> * <title>TODO</title> * <itemizedlist> * <listitem>Closed path are not yet managed: an elegant solution is not * so obvious: use <function>cpml_close_offset</function> when * will be available.</listitem> * <listitem>Degenerated primitives, such as lines of length 0, are not * managed properly.</listitem> * </itemizedlist> * </important> * * Since: 1.0 **/ void cpml_segment_offset(CpmlSegment *segment, double offset) { CpmlPrimitive primitive; CpmlPrimitive last_primitive; CpmlPair old_end; cairo_path_data_t org, *old_org; int first_cycle; cpml_primitive_from_segment(&primitive, segment); first_cycle = 1; do { if (! first_cycle) { cpml_pair_to_cairo(&old_end, &org); old_org = primitive.org; primitive.org = &org; } cpml_primitive_put_point(&primitive, -1, &old_end); cpml_primitive_offset(&primitive, offset); if (! first_cycle) { cpml_primitive_join(&last_primitive, &primitive); primitive.org = old_org; } cpml_primitive_copy(&last_primitive, &primitive); first_cycle = 0; } while (cpml_primitive_next(&primitive)); }
static void _cpml_sanity_offset(gint i) { switch (i) { case 1: cpml_primitive_offset(NULL, 1); break; default: g_test_trap_assert_failed(); break; } }
static void _cpml_method_offset(void) { CpmlSegment original, *segment; CpmlPrimitive primitive, line, curve; CpmlPrimitive *backup; /* Work on a copy to avoid modifying the original cairo path */ cpml_segment_from_cairo(&original, (cairo_path_t *) adg_test_path()); segment = cpml_segment_deep_dup(&original); cpml_primitive_from_segment(&primitive, segment); /* Offsetting and de-offsetting can introduce rounding errors * so we use adg_assert_isapprox instead of g_assert_cmpfloat */ /* Line */ cpml_primitive_copy(&line, &primitive); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((primitive.org)->point.x, 0); adg_assert_isapprox((primitive.org)->point.y, 2); adg_assert_isapprox(primitive.data[1].point.x, 3); adg_assert_isapprox(primitive.data[1].point.y, 2); cpml_primitive_offset(&primitive, -1); adg_assert_isapprox((primitive.org)->point.x, 0); adg_assert_isapprox((primitive.org)->point.y, 1); adg_assert_isapprox(primitive.data[1].point.x, 3); adg_assert_isapprox(primitive.data[1].point.y, 1); /* Arc */ cpml_primitive_next(&primitive); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((primitive.org)->point.x, 2.003); adg_assert_isapprox((primitive.org)->point.y, 0.923); adg_assert_isapprox(primitive.data[1].point.x, 3.156); adg_assert_isapprox(primitive.data[1].point.y, 5.537); adg_assert_isapprox(primitive.data[2].point.x, 5.463); adg_assert_isapprox(primitive.data[2].point.y, 7.844); cpml_primitive_offset(&primitive, -1); adg_assert_isapprox((primitive.org)->point.x, 3); adg_assert_isapprox((primitive.org)->point.y, 1); adg_assert_isapprox(primitive.data[1].point.x, 4); adg_assert_isapprox(primitive.data[1].point.y, 5); adg_assert_isapprox(primitive.data[2].point.x, 6); adg_assert_isapprox(primitive.data[2].point.y, 7); /* Curve */ cpml_primitive_next(&primitive); cpml_primitive_copy(&curve, &primitive); /* The offset algorithm for curves is an approximation, so * offsetting +1 and -1 does not return the original curve. * Keeping a backup around to restore the original data. */ backup = cpml_primitive_deep_dup(&curve); /* Testing different algorithms */ cpml_curve_offset_algorithm(CPML_CURVE_OFFSET_ALGORITHM_GEOMETRICAL); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((primitive.org)->point.x, 5.293); adg_assert_isapprox((primitive.org)->point.y, 7.707); adg_assert_isapprox(primitive.data[1].point.x, 7.889); adg_assert_isapprox(primitive.data[1].point.y, 8.515); adg_assert_isapprox(primitive.data[2].point.x, 11.196); adg_assert_isapprox(primitive.data[2].point.y, 9.007); adg_assert_isapprox(primitive.data[3].point.x, -1.4); adg_assert_isapprox(primitive.data[3].point.y, 1.2); cpml_primitive_copy_data(&primitive, backup); cpml_curve_offset_algorithm(CPML_CURVE_OFFSET_ALGORITHM_BAIOCA); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((primitive.org)->point.x, 5.293); adg_assert_isapprox((primitive.org)->point.y, 7.707); adg_assert_isapprox(primitive.data[1].point.x, 6.901); adg_assert_isapprox(primitive.data[1].point.y, 9.315); adg_assert_isapprox(primitive.data[2].point.x, 10.806); adg_assert_isapprox(primitive.data[2].point.y, 10.355); adg_assert_isapprox(primitive.data[3].point.x, -1.4); adg_assert_isapprox(primitive.data[3].point.y, 1.2); cpml_primitive_copy_data(&primitive, backup); cpml_curve_offset_algorithm(CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((primitive.org)->point.x, 5.293); adg_assert_isapprox((primitive.org)->point.y, 7.707); adg_assert_isapprox(primitive.data[1].point.x, -5.758); adg_assert_isapprox(primitive.data[1].point.y, -3.344); adg_assert_isapprox(primitive.data[2].point.x, 24.987); adg_assert_isapprox(primitive.data[2].point.y, 20.99); adg_assert_isapprox(primitive.data[3].point.x, -1.4); adg_assert_isapprox(primitive.data[3].point.y, 1.2); cpml_primitive_copy_data(&primitive, backup); g_free(backup); cpml_curve_offset_algorithm(CPML_CURVE_OFFSET_ALGORITHM_DEFAULT); /* Close: this primitive does not own data points but should * modify the points of the previous and next primitives */ cpml_primitive_next(&primitive); cpml_primitive_offset(&primitive, 1); adg_assert_isapprox((curve.org)->point.x, 6); adg_assert_isapprox((curve.org)->point.y, 7); adg_assert_isapprox(curve.data[3].point.x, -1.553); adg_assert_isapprox(curve.data[3].point.y, 2.894); adg_assert_isapprox((line.org)->point.x, 0.447); adg_assert_isapprox((line.org)->point.y, 1.894); adg_assert_isapprox(line.data[1].point.x, 3); adg_assert_isapprox(line.data[1].point.y, 1); cpml_primitive_offset(&primitive, -1); adg_assert_isapprox((curve.org)->point.x, 6); adg_assert_isapprox((curve.org)->point.y, 7); adg_assert_isapprox(curve.data[3].point.x, -2); adg_assert_isapprox(curve.data[3].point.y, 2); adg_assert_isapprox((line.org)->point.x, 0); adg_assert_isapprox((line.org)->point.y, 1); adg_assert_isapprox(line.data[1].point.x, 3); adg_assert_isapprox(line.data[1].point.y, 1); g_free(segment); }
static void _adg_do_fillet(AdgPath *path, CpmlPrimitive *current) { AdgPathPrivate *data; CpmlPrimitive *last, *current_dup, *last_dup; gdouble radius, offset, pos; CpmlPair center, vector, p[3]; data = path->data; last = &data->last; current_dup = cpml_primitive_deep_dup(current); last_dup = cpml_primitive_deep_dup(last); radius = data->operation.data.fillet.radius; offset = _adg_is_convex(last_dup, current_dup) ? -radius : radius; /* Find the center of the fillet from the intersection between * the last and current primitives offseted by radius */ cpml_primitive_offset(current_dup, offset); cpml_primitive_offset(last_dup, offset); if (cpml_primitive_put_intersections(current_dup, last_dup, 1, ¢er) == 0) { g_warning(_("%s: fillet with radius of %lf is not applicable here"), G_STRLOC, radius); g_free(current_dup); g_free(last_dup); return; } /* Compute the start point of the fillet */ pos = cpml_primitive_get_closest_pos(last_dup, ¢er); cpml_primitive_put_vector_at(last_dup, pos, &vector); cpml_vector_set_length(&vector, offset); cpml_vector_normal(&vector); p[0].x = center.x - vector.x; p[0].y = center.y - vector.y; /* Compute the mid point of the fillet */ cpml_pair_from_cairo(&vector, current->org); vector.x -= center.x; vector.y -= center.y; cpml_vector_set_length(&vector, radius); p[1].x = center.x + vector.x; p[1].y = center.y + vector.y; /* Compute the end point of the fillet */ pos = cpml_primitive_get_closest_pos(current_dup, ¢er); cpml_primitive_put_vector_at(current_dup, pos, &vector); cpml_vector_set_length(&vector, offset); cpml_vector_normal(&vector); p[2].x = center.x - vector.x; p[2].y = center.y - vector.y; g_free(current_dup); g_free(last_dup); /* Change the end point of the last primitive */ cpml_primitive_set_point(last, -1, &p[0]); /* Change the start point of the current primitive */ cpml_primitive_set_point(current, 0, &p[2]); /* Add the fillet arc */ data->operation.action = ADG_ACTION_NONE; adg_path_append(path, CPML_ARC, &p[1], &p[2]); }