利用者:Genio84/gsoc2016/code docs/
目次
Code documentation
Improved extrusion
The implementation of this tool consisted in altering the already existing BKE_curve_bevel_make
, which generates the displists for curve objects with non-zero bevel and extrude values. It also required adding two new flags for curves, CU_EXTRUDE_REV
and CU_UNI_EXTRUDE
. The code speaks for itself
if (cu->flag & CU_EXTRUDE_REV && cu->flag & CU_UNI_EXTRUDE) { fp[2] = 0.0; fp[5] = -cu->ext1; } else if (cu->flag & CU_UNI_EXTRUDE) { fp[2] = cu->ext1; fp[5] = 0.0; } else { fp[2] = -cu->ext1; fp[5] = cu->ext1; }
this is the main if
-statement that implements the operator. All other instances are variations to accommodate for different bevel settings.
Extend tool
Description
The Extend tool allows you to extend the selected spline endpoint(s) to the nearest intersection. It combines Extend functionality with Intersect one. The implementation of this tool was based on a previously existing add-on [1].
Some of the details of the implementation of this tool can be found on [2].
[2] https://wiki.blender.org/index.php/User:Genio84/gsoc2016/Extend_progress
Implementation
The code follows the original add-on very closely, fixing some bugs along the way. When the implementation is not clear, a look at the add-on may help clear some doubts. The operator name is CURVE_OT_extend_curve. The functions involved are
static void get_selected_bezier_splines(ListBase* spline_list, ListBase *nubase, int *r_number_splines, bool return_cyclic)
static ListBase *get_selected_handles(Nurb* nu, int *r_num_sel_handles)
static void get_selected_endpoints(Nurb* nu, BezTriple **r_handle_list)
static void get_nurb_shape_bounds(Curve *cu, float r_bound_box[4])
static void get_max_extent_2d(float p1[2], float p2[2], float bb[4], float r_result[2])
static void nearest_point(float p[2], ListBase *p_list, float r_near[2], int *r_result)
static ListBase *interpolate_all_segments(Nurb *nu)
static void linear_spline_list(ListBase *nubase, ListBase *spline_list)
static void get_intersections(ListBase* il, float *p1, float *p2, ListBase *nubase)
static int extend_curve_exec(bContext *C, wmOperator *op)
void CURVE_OT_extend_curve(wmOperatorType *ot)
When the operator is called, the function extend_curve_exec runs. Here is what it does:
- Get the selected splines.
- Given the number of splines selected
- Get the selected endpoints.
- Given the number of selected endpoints, get the extention point.
- Add the intersection point to the selected spline(s).
TODO
- Separate this operator into two operators: Extend and Intersect. This would allow for code reusing, for example, for the Batch Extend tool.
Batch Extend tool
Description
The Batch Extend tool allows you to extend all the selected vertices. This helps to save time since without it a user would have to select and extend each vertex individually.
Implementation
The functions associated with this operator are
static int batch_extend_exec(bContext *C, wmOperator *UNUSED(op))
void CURVE_OT_batch_extend(wmOperatorType *ot)
This operator applies the the extend operator to all selected vertices. The algorithm is the following
- Go through all splines.
- Separate them into two ListBase of LinkData: one with the splines with the first endpoint selected, and one with the splines with the last endpoint selected. Splines with both endpoints selected go to both lists.
- Deselect all handles.
- For each nu in each ListBase, select the respective endpoint, and run the Extend operator.
Trim tool
Description
The Trim tool allows you to trim the selected spline vertex/vertices. There are three trim modes: endpoints, middle points and cyclic splines. The implementation of this tool was based on a previously existing add-on [1].
Implementation
The code follows the original add-on very closely, fixing some bugs along the way. When the implementation is not clear, a look at the add-on may help clear some doubts. The operator name is CURVE_OT_trim_curve. The functions involved are
static int get_selected_spline_id(ListBase *nubase)
static int sel_point_id(Nurb *nu)
static int XShape_cmp(const void *xs1, const void *xs2)
static ListBase *spline_X_shape(Object *obedit, int selected_spline)
static bool is_between(float *x, float *a, float *b)
static float ratio_to_segment(float *x, float *p1, float *p2, float *p3, float *p4, int res)
static void split_segment(float t, float *p1, float *p2, float *p3, float *p4, float *r_s1, float *r_s2)
static void chop(float *x, float *p1, float *p2, float *p3, float *p4, int res, float *r_s1, float *r_s2)
static int trim_curve_exec(bContext *C, wmOperator *op)
void CURVE_OT_trim_curve(wmOperatorType *ot)
The chop
function can be used to add vertices to Bezier splines without altering their shape.
I also implemented the data structure XShape to record the data used in the calculations. The implementation is
typedef struct XShape { struct XShape *next, *prev; float *intersections; int order; float distance; } XShape;
It is a linked list element, containing a list with the the coordinates of an intersections (despite the 's' in the name, it only contains the coordinates of one point), the order (used to know how the spline splits) and the distance of the intersection to the control points (used to choose which intersection we are going to use).
When the operator is called, the function extend_curve_exec runs. Here is what it does:
- Get the selected spline.
- Get all the intersections with the spline (spline_X_shape).
- Split the spline points into two lists, one with the high order points and one with the low order points.
- If the spline is cyclic, chop it and replace it with the trimmed one.
- If the spline is non-cyclic, check the type of trimming we are about to perform
- Start point trimming.
- Replace the spline with another one without the start points.
- End point trimming.
- Replace the spline with another one without the end points.
- Middle point trimming.
- Replace the spline with two splines.
- Start point trimming.
TODO
- Implement batch trimming (i. e., all selected points would be trimmed).
Offset tool
Description
The Offset tool allows you to offset the selected spline. Due to the mathematical details of Bezier curves, true mathematical curve offset (i. e. where the distance between every point in the curve and its offset is equal) is not possible. This tool offers an approximation based on the Tiller-Hanson algorithm. Offset distance can be inputted in three different ways (mouse, keyboard and tool shelf).
Implementation
Some details about the implementation can be found on [1].
The functions associated with this operator are
static void get_offset_vecs(BezTriple *bezt1, BezTriple *bezt2, float *r_v1, float *r_v2)
static void get_handles_offset_vec(float *p1, float *p2, float *p3, float *r_v1)
static bool reverse_offset(BezTriple *offset1, BezTriple *offset2, ListBase *nurb_base)
static Nurb *offset_curve(Nurb *nu, ListBase *nubase, float distance)
static int offset_curve_exec(bContext *C, wmOperator *op)
static int offset_curve_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int offset_curve_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
void CURVE_OT_offset_curve(wmOperatorType *ot)
Depending on how the operator is called, the function extend_curve_exec or extend_curve_invoke runs. In any case, the core calculations are
- Get the selected spline.
- Calculate the offset vectors for each handle (as described in [1]).
- Calculate the new coordinates of the handle points.
- Check for self-intersections. These help determine if the current offset direction is incorrect.
- Make appropriate corrections.
- Add the new spline to the list of splines.
The task of the invoke
is to prepare the operator for running the modal
function, while the exec
function skips any user interaction. The modal
operator is very similar to that of the Bevel tool.
In an earlier version of the code, the intersection check was done using the get_intersections
function. However, this had the problem that, given a sufficiently large offset distance, the curve would intersect itself again, and the direction correction would trigger and give wrong results. Therefore, in the current version of the code, this check is done using vector calculus (relying heavily on the dot product). A simplified description of the algorithm can be found in [2]
[1] https://wiki.blender.org/index.php/User:Genio84/gsoc2016/offset
[2] http://computergraphics.stackexchange.com/a/3856/4843
Chamfer tool
Description
The Chamfer tool allows you to chamfer the selected point. Chamfers have two parameters: angle and length. These are currently controlled in the tool shelf, after the operator is called.
Implementation
The functions associated with this operator are
static void chamfer_handle(BezTriple *bezt, BezTriple *r_new_bezt1, BezTriple *r_new_bezt2, float theta, float d)
static int curve_chamfer_exec(bContext *C, wmOperator *op)
static int curve_chamfer_modal(bContext *C, wmOperator *op, const wmEvent *event)e)
static int curve_chamfer_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CURVE_OT_curve_chamfer(wmOperatorType *ot)
Depending on how the operator is called, the function curve_chamfer_exec or curve_chamfer_invoke runs. In any case, the core calculations are all performed in the function chamfer_handle
.
The task of the invoke
is to prepare the operator for running the modal
function, while the exec
function skips any user interaction.
Currently, the modal operator is unable to handle keyboard input, and therefore both the invoke
and modal
functions are not called.
TODO
- Finish the modal function. I had issues with numinput for the angle. When I enabled numinput for segment length, it hijacked all interaction. Probably because I don't know how to exit numinput without also exiting the operator.
- Refactor the code. It is unacceptable that the same loop (~45 lines of code) is rewritten 5 times on the whole operator code (~180 useless lines of code).
Fillet tool
Description
The fillet tool allows the user to fillet the selected points. A fillet means approximating the point locally as a circle.
Implementation
This operator is very similar to the chamfer operator.
The functions associated with this operator are
static void fillet_handle(BezTriple *bezt, BezTriple *r_new_bezt1, BezTriple *r_new_bezt2, float theta, float r)
static int curve_fillet_exec(bContext *C, wmOperator *op)
void CURVE_OT_curve_fillet(wmOperatorType *ot)
The core calculations are made on fillet_handle
.
TODO
- Finish the modal function. This one sets two coordinates to NaN.
- Refactor the code. It is unacceptable that the same loop (~45 lines of code) is rewritten 5 times on the whole operator code (~180 useless lines of code).