Example #1
0
    def remove_knot(self, u, count=1, target=None, if_possible=False):
        if (count is None) == (target is None):
            raise Exception("Either count or target must be specified")

        knotvector = self.get_knotvector()
        orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u)
        if count == SvNurbsCurve.ALL:
            count = orig_multiplicity
        elif count == SvNurbsCurve.ALL_BUT_ONE:
            count = orig_multiplicity - 1
        elif count is None:
            count = orig_multiplicity - target

        curve = self.copy()
        curve = operations.remove_knot(curve.curve, [u], [count])
        result = SvGeomdlCurve(curve)
        result.u_bounds = self.u_bounds

        new_kv = result.get_knotvector()
        new_multiplicity = sv_knotvector.find_multiplicity(new_kv, u)
        if not if_possible and (orig_multiplicity - count < new_multiplicity):
            raise CantRemoveKnotException(
                f"Asked to remove knot t={u} for {count} times, but could remove it only {orig_multiplicity - count} times"
            )

        return result
Example #2
0
    def insert_knot(self, u_bar, count=1):
        # "The NURBS book", 2nd edition, p.5.2, eq. 5.11
        s = sv_knotvector.find_multiplicity(self.knotvector, u_bar)
        #print(f"I: kv {len(self.knotvector)}{self.knotvector}, u_bar {u_bar} => s {s}")
        k = np.searchsorted(self.knotvector, u_bar, side='right') - 1
        p = self.degree
        u = self.knotvector
        new_knotvector = sv_knotvector.insert(self.knotvector, u_bar, count)
        N = len(self.control_points)
        control_points = self.get_homogenous_control_points()

        for r in range(1, count + 1):
            prev_control_points = control_points
            control_points = []
            for i in range(N + 1):
                #print(f"I: i {i}, k {k}, p {p}, r {r}, s {s}, k-p+r-1 {k-p+r-1}, k-s {k-s}")
                if i <= k - p + r - 1:
                    point = prev_control_points[i]
                    #print(f"P[{r},{i}] := {i}{prev_control_points[i]}")
                elif k - p + r <= i <= k - s:
                    denominator = u[i + p - r + 1] - u[i]
                    alpha = (u_bar - u[i]) / denominator
                    point = alpha * prev_control_points[i] + (
                        1.0 - alpha) * prev_control_points[i - 1]
                    #print(f"P[{r},{i}]: alpha {alpha}, pts {i}{prev_control_points[i]}, {i-1}{prev_control_points[i-1]} => {point}")
                else:
                    point = prev_control_points[i - 1]
                    #print(f"P[{r},{i}] := {i-1}{prev_control_points[i-1]}")
                control_points.append(point)
            N += 1

        control_points, weights = from_homogenous(np.array(control_points))
        curve = SvNativeNurbsCurve(self.degree, new_knotvector, control_points,
                                   weights)
        return curve
Example #3
0
    def test_remove_2(self):
        points = np.array(
            [[0, 0, 0], [0, 1, 0], [1, 2, 0], [2, 2, 0], [3, 1, 0], [3, 0, 0]],
            dtype=np.float64)
        degree = 3
        kv = np.array([0, 0, 0, 0, 0.25, 0.75, 1, 1, 1, 1])
        weights = [1, 1, 1, 1, 1, 1]
        curve = SvNativeNurbsCurve(degree, kv, points, weights)
        kv_err = sv_knotvector.check(degree, kv, len(points))
        if kv_err is not None:
            raise Exception(kv_err)
        knot = 0.1
        inserted = curve.insert_knot(knot, 1)

        self.assertEquals(len(inserted.get_control_points()), len(points) + 1)

        expected_inserted_kv = np.array(
            [0, 0, 0, 0, 0.1, 0.25, 0.75, 1, 1, 1, 1])
        self.assert_numpy_arrays_equal(inserted.get_knotvector(),
                                       expected_inserted_kv,
                                       precision=8)

        inserted_kv = inserted.get_knotvector()
        k = np.searchsorted(inserted_kv, knot, side='right') - 1
        s = sv_knotvector.find_multiplicity(inserted_kv, knot)
        print("K:", k, "S:", s)
        removed = inserted.remove_knot(knot, 1)
        self.assert_numpy_arrays_equal(removed.get_knotvector(),
                                       kv,
                                       precision=8)
Example #4
0
    def split_at(self, t):
        current_multiplicity = sv_knotvector.find_multiplicity(self.knotvector, t)
        to_add = self.degree - current_multiplicity # + 1
        curve = self.insert_knot(t, count=to_add)
        knot_span = np.searchsorted(curve.knotvector, t)

        ts = np.full((self.degree+1,), t)
        knotvector1 = np.concatenate((curve.knotvector[:knot_span], ts))
        knotvector2 = np.insert(curve.knotvector[knot_span:], 0, t)

        control_points_1 = curve.control_points[:knot_span]
        control_points_2 = curve.control_points[knot_span-1:]
        weights_1 = curve.weights[:knot_span]
        weights_2 = curve.weights[knot_span-1:]

        kv_error = sv_knotvector.check(curve.degree, knotvector1, len(control_points_1))
        if kv_error is not None:
            raise Exception(kv_error)
        kv_error = sv_knotvector.check(curve.degree, knotvector2, len(control_points_2))
        if kv_error is not None:
            raise Exception(kv_error)

        curve1 = SvNativeNurbsCurve(curve.degree, knotvector1,
                    control_points_1, weights_1)
        curve2 = SvNativeNurbsCurve(curve.degree, knotvector2,
                    control_points_2, weights_2)
        return curve1, curve2
Example #5
0
    def insert_knot(self, u_bar, count=1, if_possible=False):
        # "The NURBS book", 2nd edition, p.5.2, eq. 5.11
        N = len(self.control_points)
        u = self.get_knotvector()
        s = sv_knotvector.find_multiplicity(u, u_bar)
        #print(f"I: kv {len(u)}{u}, u_bar {u_bar} => s {s}")
        #k = np.searchsorted(u, u_bar, side='right')-1
        k = sv_knotvector.find_span(u, N, u_bar)
        p = self.get_degree()
        new_knotvector = sv_knotvector.insert(u, u_bar, count)
        control_points = self.get_homogenous_control_points()

        if (u_bar == u[0] or u_bar == u[-1]):
            if s + count > p + 1:
                if if_possible:
                    count = (p + 1) - s
                else:
                    raise CantInsertKnotException(
                        f"Can't insert first/last knot t={u_bar} for {count} times"
                    )
        else:
            if s + count > p:
                if if_possible:
                    count = p - s
                else:
                    raise CantInsertKnotException(
                        f"Can't insert knot t={u_bar} for {count} times")

        for r in range(1, count + 1):
            prev_control_points = control_points
            control_points = []
            for i in range(N + 1):
                #print(f"I: i {i}, k {k}, p {p}, r {r}, s {s}, k-p+r-1 {k-p+r-1}, k-s {k-s}")
                if i <= k - p + r - 1:
                    point = prev_control_points[i]
                    #print(f"P[{r},{i}] := {i}{prev_control_points[i]}")
                elif k - p + r <= i <= k - s:
                    denominator = u[i + p - r + 1] - u[i]
                    if abs(denominator) < 1e-6:
                        raise Exception(
                            f"Can't insert the knot t={u_bar} for {i}th time: u[i+p-r+1]={u[i+p-r+1]}, u[i]={u[i]}, denom={denominator}"
                        )
                    alpha = (u_bar - u[i]) / denominator
                    point = alpha * prev_control_points[i] + (
                        1.0 - alpha) * prev_control_points[i - 1]
                    #print(f"P[{r},{i}]: alpha {alpha}, pts {i}{prev_control_points[i]}, {i-1}{prev_control_points[i-1]} => {point}")
                else:
                    point = prev_control_points[i - 1]
                    #print(f"P[{r},{i}] := {i-1}{prev_control_points[i-1]}")
                control_points.append(point)
            N += 1

        control_points, weights = from_homogenous(np.array(control_points))
        curve = SvNativeNurbsCurve(self.degree, new_knotvector, control_points,
                                   weights)
        return curve
Example #6
0
    def _split_at(self, t):
        # Split without building SvNurbsCurve objects:
        # Some implementations (geomdl in particular)
        # can check number of control points vs curve degree,
        # and that can be bad for very small segments;
        # on the other hand, we may not care about it
        # if we are throwing away that small segment and
        # going to use only the bigger one.

        t_min, t_max = self.get_u_bounds()

        # corner cases
        if t <= t_min:
            return None, (self.get_knotvector(), self.get_control_points(),
                          self.get_weights())
        if t >= t_max:
            return (self.get_knotvector(), self.get_control_points(),
                    self.get_weights()), None

        current_multiplicity = sv_knotvector.find_multiplicity(
            self.get_knotvector(), t)
        to_add = self.get_degree() - current_multiplicity  # + 1
        curve = self.insert_knot(t, count=to_add)
        knot_span = np.searchsorted(curve.get_knotvector(), t)

        ts = np.full((self.get_degree() + 1, ), t)
        knotvector1 = np.concatenate((curve.get_knotvector()[:knot_span], ts))
        knotvector2 = np.insert(curve.get_knotvector()[knot_span:], 0, t)

        control_points_1 = curve.get_control_points()[:knot_span]
        control_points_2 = curve.get_control_points()[knot_span - 1:]
        weights_1 = curve.get_weights()[:knot_span]
        weights_2 = curve.get_weights()[knot_span - 1:]

        #print(f"S: ctlpts1: {len(control_points_1)}, 2: {len(control_points_2)}")
        kv_error = sv_knotvector.check(curve.get_degree(), knotvector1,
                                       len(control_points_1))
        if kv_error is not None:
            raise Exception(kv_error)
        kv_error = sv_knotvector.check(curve.get_degree(), knotvector2,
                                       len(control_points_2))
        if kv_error is not None:
            raise Exception(kv_error)

        curve1 = (knotvector1, control_points_1, weights_1)
        curve2 = (knotvector2, control_points_2, weights_2)
        return curve1, curve2
Example #7
0
        def remove_one_knot(curve):
            ctrlpts = curve.get_homogenous_control_points()
            N = len(ctrlpts)
            knotvector = curve.get_knotvector()
            orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u)
            knot_span = sv_knotvector.find_span(knotvector, N, u)

            # Initialize variables
            first = knot_span - degree
            last = knot_span - orig_multiplicity

            # Don't change input variables, prepare new ones for updating
            ctrlpts_new = deepcopy(ctrlpts)

            # Initialize temp array for storing new control points
            temp_i = np.zeros((2 * degree + 1, 4))
            temp_j = np.zeros((2 * degree + 1, 4))

            removed_count = 0
            # Loop for Eqs 5.28 & 5.29
            t = 0
            offset = first - 1  # difference in index between `temp` and ctrlpts
            temp_i[0] = ctrlpts[offset]
            temp_j[last + 1 - offset] = ctrlpts[last + 1]
            i = first
            j = last
            ii = 1
            jj = last - offset
            can_remove = False

            # Compute control points for one removal step
            while j - i > t:
                alpha_i = knot_removal_alpha_i(u, knotvector, i)
                alpha_j = knot_removal_alpha_j(u, knotvector, j)

                temp_i[ii] = (ctrlpts[i] -
                              (1.0 - alpha_i) * temp_i[ii - 1]) / alpha_i
                temp_j[jj] = (ctrlpts[j] -
                              alpha_j * temp_j[jj + 1]) / (1.0 - alpha_j)

                i += 1
                j -= 1
                ii += 1
                jj -= 1

            # Check if the knot is removable
            if j - i < t:
                dist = point_distance(temp_i[ii - 1], temp_j[jj + 1])
                if dist <= tolerance:
                    can_remove = True
                else:
                    logger.debug(f"remove_knot: stop, distance={dist}")
            else:
                alpha_i = knot_removal_alpha_i(u, knotvector, i)
                ptn = alpha_i * temp_j[ii + t + 1] + (1.0 -
                                                      alpha_i) * temp_i[ii - 1]
                dist = point_distance(ctrlpts[i], ptn)
                if dist <= tolerance:
                    can_remove = True
                else:
                    logger.debug(f"remove_knot: stop, distance={dist}")

            # Check if we can remove the knot and update new control points array
            if can_remove:
                i = first
                j = last
                while j - i > t:
                    ctrlpts_new[i] = temp_i[i - offset]
                    ctrlpts_new[j] = temp_j[j - offset]
                    i += 1
                    j -= 1
                # Update indices
                first -= 1
                last += 1
                removed_count += 1

            else:
                raise CantRemoveKnotException()

            new_kv = np.copy(curve.get_knotvector())

            if removed_count > 0:
                m = N + degree + 1
                for k in range(knot_span + 1, m):
                    new_kv[k - removed_count] = new_kv[k]
                new_kv = new_kv[:m - removed_count]
                #new_kv = np.delete(curve.get_knotvector(), np.s_[(r-t+1):(r+1)])

                # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition)
                j = int((2 * knot_span - orig_multiplicity - degree) /
                        2)  # first control point out
                i = j
                for k in range(1, removed_count):
                    if k % 2 == 1:
                        i += 1
                    else:
                        j -= 1
                for k in range(i + 1, N):
                    ctrlpts_new[j] = ctrlpts_new[k]
                    j += 1

                # Slice to get the new control points
                ctrlpts_new = ctrlpts_new[0:-removed_count]

            ctrlpts_new = np.array(ctrlpts_new)
            control_points, weights = from_homogenous(ctrlpts_new)

            return curve.copy(knotvector=new_kv,
                              control_points=control_points,
                              weights=weights)
Example #8
0
    def remove_knot(self,
                    u,
                    count=1,
                    target=None,
                    tolerance=1e-6,
                    if_possible=False):
        # Implementation adapted from Geomdl
        logger = getLogger()

        if (count is None) == (target is None):
            raise Exception("Either count or target must be specified")

        orig_multiplicity = sv_knotvector.find_multiplicity(
            self.get_knotvector(), u)

        if count == SvNurbsCurve.ALL:
            count = orig_multiplicity
        elif count == SvNurbsCurve.ALL_BUT_ONE:
            count = orig_multiplicity - 1
        elif count is None:
            count = orig_multiplicity - target

        degree = self.get_degree()
        order = degree + 1

        if not if_possible and (count > orig_multiplicity):
            raise CantRemoveKnotException(
                f"Asked to remove knot t={u} for {count} times, but it's multiplicity is only {orig_multiplicity}"
            )

        # Edge case
        if count < 1:
            return self

        def knot_removal_alpha_i(u, knotvector, idx):
            return (u - knotvector[idx]) / (knotvector[idx + order] -
                                            knotvector[idx])

        def knot_removal_alpha_j(u, knotvector, idx):
            return (u - knotvector[idx]) / (knotvector[idx + order] -
                                            knotvector[idx])

        def point_distance(p1, p2):
            return np.linalg.norm(p1 - p2)
            #return np.linalg.norm(np.array(p1) - np.array(p2))

        def remove_one_knot(curve):
            ctrlpts = curve.get_homogenous_control_points()
            N = len(ctrlpts)
            knotvector = curve.get_knotvector()
            orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u)
            knot_span = sv_knotvector.find_span(knotvector, N, u)

            # Initialize variables
            first = knot_span - degree
            last = knot_span - orig_multiplicity

            # Don't change input variables, prepare new ones for updating
            ctrlpts_new = deepcopy(ctrlpts)

            # Initialize temp array for storing new control points
            temp_i = np.zeros((2 * degree + 1, 4))
            temp_j = np.zeros((2 * degree + 1, 4))

            removed_count = 0
            # Loop for Eqs 5.28 & 5.29
            t = 0
            offset = first - 1  # difference in index between `temp` and ctrlpts
            temp_i[0] = ctrlpts[offset]
            temp_j[last + 1 - offset] = ctrlpts[last + 1]
            i = first
            j = last
            ii = 1
            jj = last - offset
            can_remove = False

            # Compute control points for one removal step
            while j - i > t:
                alpha_i = knot_removal_alpha_i(u, knotvector, i)
                alpha_j = knot_removal_alpha_j(u, knotvector, j)

                temp_i[ii] = (ctrlpts[i] -
                              (1.0 - alpha_i) * temp_i[ii - 1]) / alpha_i
                temp_j[jj] = (ctrlpts[j] -
                              alpha_j * temp_j[jj + 1]) / (1.0 - alpha_j)

                i += 1
                j -= 1
                ii += 1
                jj -= 1

            # Check if the knot is removable
            if j - i < t:
                dist = point_distance(temp_i[ii - 1], temp_j[jj + 1])
                if dist <= tolerance:
                    can_remove = True
                else:
                    logger.debug(f"remove_knot: stop, distance={dist}")
            else:
                alpha_i = knot_removal_alpha_i(u, knotvector, i)
                ptn = alpha_i * temp_j[ii + t + 1] + (1.0 -
                                                      alpha_i) * temp_i[ii - 1]
                dist = point_distance(ctrlpts[i], ptn)
                if dist <= tolerance:
                    can_remove = True
                else:
                    logger.debug(f"remove_knot: stop, distance={dist}")

            # Check if we can remove the knot and update new control points array
            if can_remove:
                i = first
                j = last
                while j - i > t:
                    ctrlpts_new[i] = temp_i[i - offset]
                    ctrlpts_new[j] = temp_j[j - offset]
                    i += 1
                    j -= 1
                # Update indices
                first -= 1
                last += 1
                removed_count += 1

            else:
                raise CantRemoveKnotException()

            new_kv = np.copy(curve.get_knotvector())

            if removed_count > 0:
                m = N + degree + 1
                for k in range(knot_span + 1, m):
                    new_kv[k - removed_count] = new_kv[k]
                new_kv = new_kv[:m - removed_count]
                #new_kv = np.delete(curve.get_knotvector(), np.s_[(r-t+1):(r+1)])

                # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition)
                j = int((2 * knot_span - orig_multiplicity - degree) /
                        2)  # first control point out
                i = j
                for k in range(1, removed_count):
                    if k % 2 == 1:
                        i += 1
                    else:
                        j -= 1
                for k in range(i + 1, N):
                    ctrlpts_new[j] = ctrlpts_new[k]
                    j += 1

                # Slice to get the new control points
                ctrlpts_new = ctrlpts_new[0:-removed_count]

            ctrlpts_new = np.array(ctrlpts_new)
            control_points, weights = from_homogenous(ctrlpts_new)

            return curve.copy(knotvector=new_kv,
                              control_points=control_points,
                              weights=weights)

        curve = self
        removed_count = 0
        for i in range(count):
            try:
                curve = remove_one_knot(curve)
                removed_count += 1
            except CantRemoveKnotException as e:
                break

        if not if_possible and (removed_count < count):
            raise CantRemoveKnotException(
                f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times"
            )
        #print(f"Removed knot t={u} for {removed_count} times")
        return curve