diff --git a/lab2/ibspline.py b/lab2/ibspline.py index 7c88030..f4f9eb2 100644 --- a/lab2/ibspline.py +++ b/lab2/ibspline.py @@ -20,6 +20,7 @@ def ibspline(inputA, inputB=None): global save_message_xloc global num_sample global curve_degree, knot_vector, add_cp_mode # added for Bspline + global ax_lr_button, lr_button # added for Lane-Riesenfeld # de Boor's algorithm to evaluate a point on a B-spline. # Returns (x, y) coordinate at parameter t @@ -119,6 +120,7 @@ def ibspline(inputA, inputB=None): plt.plot(vX, vY, 'ob', picker=True, pickradius=5) return + # Redraw the entire plot def redrawPlot(vX, vY): global text_message, ax @@ -187,6 +189,58 @@ def ibspline(inputA, inputB=None): # CallbackS + # Lane-Riesenfeld subdivision callback + def lrSubdivideCallback(event): + global vX, vY, curve_degree, knot_vector + + if len(vX) == 0: return + + # Check if knot vector is uniform + is_uniform = True + if len(knot_vector) > 1: + diff = knot_vector[1] - knot_vector[0] + for i in range(1, len(knot_vector) - 1): + if abs((knot_vector[i+1] - knot_vector[i]) - diff) > 1e-5: + is_uniform = False + break + + if not is_uniform: + outputPlotMessage("Error: LR-Subdivide requires uniform knot distances.") + plt.draw() + return + + n = curve_degree + m = len(vX) + points = list(zip(vX, vY)) + + # Step 1: Double the points (P0, P0, P1, P1, ...) + doubled_points = [] + for p in points: + doubled_points.append(p) + doubled_points.append(p) + + # Step 2: Apply moving average n times + for _ in range(n): + smoothed_points = [] + # Average adjacent points + for i in range(len(doubled_points) - 1): + px = (doubled_points[i][0] + doubled_points[i+1][0]) / 2.0 + py = (doubled_points[i][1] + doubled_points[i+1][1]) / 2.0 + smoothed_points.append((px, py)) + doubled_points = smoothed_points + + # Update vX and vY + vX = [p[0] for p in doubled_points] + vY = [p[1] for p in doubled_points] + + # update knot : maintain uniformity but increase density + new_m = len(vX) + knot_vector = list(range(1, new_m + curve_degree + 1)) + + outputPlotMessage(f"LR-Subdivide applied. (m={len(vX)})") + redrawPlot(vX, vY) + return + # callback of add cp def addCPButtonCallback(event): global add_cp_mode @@ -265,6 +319,7 @@ def ibspline(inputA, inputB=None): def createButtons(left_pos, button_width, button_height): global ax_add_button, add_button global ax_save_button, save_button + global ax_lr_button, lr_button # Colors inactive_color = 'lightgray' @@ -276,6 +331,13 @@ def ibspline(inputA, inputB=None): add_button.color = inactive_color add_button.hovercolor = inactive_color + # LR-Subdivide + ax_lr_button = plt.axes([left_pos, 0.7, button_width, button_height]) + lr_button = Button(ax_lr_button, "LR-Sub") + lr_button.on_clicked(lrSubdivideCallback) + lr_button.color = inactive_color + lr_button.hovercolor = inactive_color + # Save ax_save_button = plt.axes([left_pos, 0.6, button_width, button_height]) save_button = Button(ax_save_button, "Save") @@ -288,10 +350,12 @@ def ibspline(inputA, inputB=None): # not enable all buttons as smooth needs subdivision first def enableButtons(): - global add_button, save_button + global add_button, save_button, lr_button add_button.color = 'white' add_button.hovercolor = 'green' + lr_button.color = 'white' + lr_button.hovercolor = 'green' save_button.color = 'white' save_button.hovercolor = 'green' plt.draw()