Exemple #1
0
def register():
    global logger
    logger = getLogger("menu")
    menu, node_count, original_categories = make_categories()
    temp_details['node_count'] = node_count

    if hasattr(bpy.types, "SV_PT_NodesTPanel"):
        unregister_node_panels()

    categories = [(category.identifier, category.name, category.name, i)
                  for i, category in enumerate(menu)]
    bpy.types.Scene.sv_selected_category = bpy.props.EnumProperty(
        name="Category",
        description="Select nodes category",
        items=get_all_categories(categories))
    bpy.types.Scene.sv_node_search = bpy.props.StringProperty(
        name="Search",
        description=
        "Enter search term and press Enter to search; clear the field to return to selection of node category."
    )

    bpy.utils.register_class(SvResetNodeSearchOperator)
    register_node_panels("SVERCHOK", menu)

    build_help_remap(original_categories)
Exemple #2
0
def _intersect_curves_line(curve1, curve2, precision=0.001, logger=None):
    if logger is None:
        logger = getLogger()

    t1_min, t1_max = curve1.get_u_bounds()
    t2_min, t2_max = curve2.get_u_bounds()

    v1, v2 = _get_curve_direction(curve1)
    v3, v4 = _get_curve_direction(curve2)

    logger.debug(f"Call L: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")
    r = intersect_segment_segment(v1,
                                  v2,
                                  v3,
                                  v4,
                                  tolerance=precision,
                                  endpoint_tolerance=0.0)
    if not r:
        logger.debug(f"({v1} - {v2}) x ({v3} - {v4}): no intersection")
        return []
    else:
        u, v, pt = r
        t1 = (1 - u) * t1_min + u * t1_max
        t2 = (1 - v) * t2_min + v * t2_max
        return [(t1, t2, pt)]
Exemple #3
0
def intersect_nurbs_curves(curve1,
                           curve2,
                           method='SLSQP',
                           numeric_precision=0.001,
                           logger=None):
    if logger is None:
        logger = getLogger()

    bbox_tolerance = 1e-4

    # "Recursive bounding box" algorithm:
    # * if bounding boxes of two curves do not intersect, then curves do not intersect
    # * Otherwise, split each curves in half, and check if bounding boxes of these halves intersect.
    # * When this subdivision gives very small parts of curves, try to find intersections numerically.
    #
    # This implementation depends heavily on the fact that curves are NURBS. Because only NURBS curves
    # give us a simple way to calculate bounding box of the curve: it's a bounding box of curve's
    # control points.

    def _intersect(curve1, curve2, c1_bounds, c2_bounds):
        if curve1 is None or curve2 is None:
            return []

        t1_min, t1_max = c1_bounds
        t2_min, t2_max = c2_bounds

        #logger.debug(f"check: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")

        bbox1 = curve1.get_bounding_box().increase(bbox_tolerance)
        bbox2 = curve2.get_bounding_box().increase(bbox_tolerance)
        if not bbox1.intersects(bbox2):
            return []

        THRESHOLD = 0.01

        if bbox1.size() < THRESHOLD and bbox2.size() < THRESHOLD:
            #if _check_is_line(curve1) and _check_is_line(curve2):
            return _intersect_curves_equation(curve1,
                                              curve2,
                                              method=method,
                                              precision=numeric_precision)

        mid1 = (t1_min + t1_max) * 0.5
        mid2 = (t2_min + t2_max) * 0.5

        c11, c12 = curve1.split_at(mid1)
        c21, c22 = curve2.split_at(mid2)

        r1 = _intersect(c11, c21, (t1_min, mid1), (t2_min, mid2))
        r2 = _intersect(c11, c22, (t1_min, mid1), (mid2, t2_max))
        r3 = _intersect(c12, c21, (mid1, t1_max), (t2_min, mid2))
        r4 = _intersect(c12, c22, (mid1, t1_max), (mid2, t2_max))

        return r1 + r2 + r3 + r4

    return _intersect(curve1, curve2, curve1.get_u_bounds(),
                      curve2.get_u_bounds())
Exemple #4
0
def _intersect_curves_equation(curve1,
                               curve2,
                               method='SLSQP',
                               precision=0.001,
                               logger=None):
    if logger is None:
        logger = getLogger()

    t1_min, t1_max = curve1.get_u_bounds()
    t2_min, t2_max = curve2.get_u_bounds()

    def goal(ts):
        p1 = curve1.evaluate(ts[0])
        p2 = curve2.evaluate(ts[1])
        r = (p2 - p1).max()
        return r
        #return np.array([r, r])

    mid1 = (t1_min + t1_max) * 0.5
    mid2 = (t2_min + t2_max) * 0.5

    x0 = np.array([mid1, mid2])

    #     def callback(ts, rs):
    #         logger.debug(f"=> {ts} => {rs}")

    #logger.debug(f"Call R: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")

    # Find minimum distance between two curves with a numeric method.
    # If this minimum distance is small enough, we will say that curves
    # do intersect.
    res = scipy.optimize.minimize(goal,
                                  x0,
                                  method=method,
                                  bounds=[(t1_min, t1_max), (t2_min, t2_max)],
                                  tol=0.5 * precision)
    if res.success:
        t1, t2 = tuple(res.x)
        t1 = np.clip(t1, t1_min, t1_max)
        t2 = np.clip(t2, t2_min, t2_max)
        pt1 = curve1.evaluate(t1)
        pt2 = curve2.evaluate(t2)
        dist = np.linalg.norm(pt2 - pt1)
        if dist < precision:
            #logger.debug(f"Found: T1 {t1}, T2 {t2}, Pt1 {pt1}, Pt2 {pt2}")
            pt = (pt1 + pt2) * 0.5
            return [(t1, t2, pt)]
        else:
            logger.debug(
                f"numeric method found a point, but it's too far: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]: {dist}"
            )
            return []
    else:
        logger.debug(
            f"numeric method fail: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]: {res.message}"
        )
        return []
Exemple #5
0
 def add_fail(self, fail_name, source=None):
     """Increase counter of given fail message, also printing error message in debug mode"""
     try:
         yield
     except Exception as e:
         self._log[fail_name] += 1
         logger = getLogger()
         if logger.isEnabledFor(logging.DEBUG):
             logger.debug(f'FAIL: "{fail_name}", {"SOURCE: " if source else ""}{source or ""}, {e}')
             traceback.print_exc()
Exemple #6
0
def _intersect_curves_equation(curve1,
                               curve2,
                               method='SLSQP',
                               precision=0.001,
                               logger=None):
    if logger is None:
        logger = getLogger()

    t1_min, t1_max = curve1.get_u_bounds()
    t2_min, t2_max = curve2.get_u_bounds()

    lower = np.array([t1_min, t2_min])
    upper = np.array([t1_max, t2_max])

    def linear_intersection():
        # If both curves look very much like straight line segments,
        # then we can calculate their intersections by solving simple
        # linear equations.
        line1 = _check_is_line(curve1)
        line2 = _check_is_line(curve2)

        if line1 and line2:
            v1, v2 = line1
            v3, v4 = line2
            logger.debug(
                f"Call L: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")
            r = intersect_segment_segment(v1, v2, v3, v4)
            if not r:
                logger.debug(f"({v1} - {v2}) x ({v3} - {v4}): no intersection")
                return None
            else:
                u, v, pt = r
                t1 = (1 - u) * t1_min + u * t1_max
                t2 = (1 - v) * t2_min + v * t2_max
                return [(t1, t2, pt)]

    r = linear_intersection()
    if r is not None:
        return r

    def goal(ts):
        p1 = curve1.evaluate(ts[0])
        p2 = curve2.evaluate(ts[1])
        r = (p2 - p1).max()
        return r
        #return np.array([r, r])

    mid1 = (t1_min + t1_max) * 0.5
    mid2 = (t2_min + t2_max) * 0.5

    x0 = np.array([mid1, mid2])

    #     def callback(ts, rs):
    #         logger.debug(f"=> {ts} => {rs}")

    #logger.debug(f"Call R: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")

    # Find minimum distance between two curves with a numeric method.
    # If this minimum distance is small enough, we will say that curves
    # do intersect.
    res = scipy.optimize.minimize(goal,
                                  x0,
                                  method=method,
                                  bounds=[(t1_min, t1_max), (t2_min, t2_max)],
                                  tol=0.5 * precision)
    if res.success:
        t1, t2 = tuple(res.x)
        t1 = np.clip(t1, t1_min, t1_max)
        t2 = np.clip(t2, t2_min, t2_max)
        pt1 = curve1.evaluate(t1)
        pt2 = curve2.evaluate(t2)
        dist = np.linalg.norm(pt2 - pt1)
        if dist < precision:
            #logger.debug(f"Found: T1 {t1}, T2 {t2}, Pt1 {pt1}, Pt2 {pt2}")
            pt = (pt1 + pt2) * 0.5
            return [(t1, t2, pt)]
        else:
            logger.debug(
                f"numeric method found a point, but it's too far: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]: {dist}"
            )
            return []
    else:
        logger.debug(
            f"numeric method fail: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]: {res.message}"
        )
        return []
Exemple #7
0
def nearest_point_on_curve(src_points,
                           curve,
                           samples=10,
                           precise=True,
                           method='Brent',
                           output_points=True,
                           logger=None):
    """
    Find nearest point on any curve.
    """
    if logger is None:
        logger = getLogger()

    t_min, t_max = curve.get_u_bounds()

    def init_guess(curve, points_from):
        us = np.linspace(t_min, t_max, num=samples)

        points = curve.evaluate_array(us).tolist()
        #print("P:", points)

        kdt = kdtree.KDTree(len(us))
        for i, v in enumerate(points):
            kdt.insert(v, i)
        kdt.balance()

        us_out = []
        nearest_out = []
        for point_from in points_from:
            nearest, i, distance = kdt.find(point_from)
            us_out.append(us[i])
            nearest_out.append(tuple(nearest))

        return us_out, np.array(nearest_out)

    def goal(t):
        dv = curve.evaluate(t) - np.array(src_point)
        return np.linalg.norm(dv)

    init_ts, init_points = init_guess(curve, src_points)
    result_ts = []
    if precise:
        for src_point, init_t, init_point in zip(src_points, init_ts,
                                                 init_points):
            delta_t = (t_max - t_min) / samples
            logger.debug("T_min %s, T_max %s, init_t %s, delta_t %s", t_min,
                         t_max, init_t, delta_t)
            if init_t <= t_min:
                if init_t - delta_t >= t_min:
                    bracket = (init_t - delta_t, init_t, t_max)
                else:
                    bracket = None  # (t_min, t_min + delta_t, t_min + 2*delta_t)
            elif init_t >= t_max:
                if init_t + delta_t <= t_max:
                    bracket = (t_min, init_t, init_t + delta_t)
                else:
                    bracket = None  # (t_max - 2*delta_t, t_max - delta_t, t_max)
            else:
                bracket = (t_min, init_t, t_max)
            result = minimize_scalar(goal,
                                     bounds=(t_min, t_max),
                                     bracket=bracket,
                                     method=method)

            if not result.success:
                if hasattr(result, 'message'):
                    message = result.message
                else:
                    message = repr(result)
                raise Exception(
                    "Can't find the nearest point for {}: {}".format(
                        src_point, message))

            t0 = result.x
            if t0 < t_min:
                t0 = t_min
            elif t0 > t_max:
                t0 = t_max
            result_ts.append(t0)
    else:
        result_ts = init_ts

    if output_points:
        if precise:
            result_points = curve.evaluate_array(np.array(result_ts))
            return list(zip(result_ts, result_points))
        else:
            return list(zip(result_ts, init_points))
    else:
        return result_ts
Exemple #8
0
def register():
    global logger
    bpy.utils.register_class(SvNodeRefreshFromTextEditor)
    logger = getLogger("text_editor_plugins")
    add_keymap()
Exemple #9
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
Exemple #10
0
def intersect_nurbs_curves(curve1,
                           curve2,
                           method='SLSQP',
                           numeric_precision=0.001,
                           logger=None):
    if logger is None:
        logger = getLogger()

    u1_min, u1_max = curve1.get_u_bounds()
    u2_min, u2_max = curve2.get_u_bounds()

    expected_subdivisions = 10

    max_dt1 = (u1_max - u1_min) / expected_subdivisions
    max_dt2 = (u2_max - u2_min) / expected_subdivisions

    # Float precision problems workaround
    bbox_tolerance = 1e-4

    # "Recursive bounding box" algorithm:
    # * if bounding boxes of two curves do not intersect, then curves do not intersect
    # * Otherwise, split each curves in half, and check if bounding boxes of these halves intersect.
    # * When this subdivision gives very small parts of curves, try to find intersections numerically.
    #
    # This implementation depends heavily on the fact that curves are NURBS. Because only NURBS curves
    # give us a simple way to calculate bounding box of the curve: it's a bounding box of curve's
    # control points.

    def _intersect(curve1, curve2, c1_bounds, c2_bounds, i=0):
        if curve1 is None or curve2 is None:
            return []

        t1_min, t1_max = c1_bounds
        t2_min, t2_max = c2_bounds

        #logger.debug(f"check: [{t1_min} - {t1_max}] x [{t2_min} - {t2_max}]")

        bbox1 = curve1.get_bounding_box().increase(bbox_tolerance)
        bbox2 = curve2.get_bounding_box().increase(bbox_tolerance)
        if not bbox1.intersects(bbox2):
            return []

        r = _intersect_endpoints(curve1, curve2, numeric_precision)
        if r:
            logger.debug(
                "Endpoint intersection after %d iterations; bbox1: %s, bbox2: %s",
                i, bbox1.size(), bbox2.size())
            return [r]

        THRESHOLD = 0.02

        if curve1.is_line(numeric_precision) and curve2.is_line(
                numeric_precision):
            logger.debug("Calling Lin() after %d iterations", i)
            r = _intersect_curves_line(curve1,
                                       curve2,
                                       numeric_precision,
                                       logger=logger)
            if r:
                return r

        if bbox1.size() < THRESHOLD and bbox2.size() < THRESHOLD:
            logger.debug("Calling Eq() after %d iterations", i)
            return _intersect_curves_equation(curve1,
                                              curve2,
                                              method=method,
                                              precision=numeric_precision,
                                              logger=logger)

        mid1 = (t1_min + t1_max) * 0.5
        mid2 = (t2_min + t2_max) * 0.5

        c11, c12 = curve1.split_at(mid1)
        c21, c22 = curve2.split_at(mid2)

        r1 = _intersect(c11, c21, (t1_min, mid1), (t2_min, mid2), i + 1)
        r2 = _intersect(c11, c22, (t1_min, mid1), (mid2, t2_max), i + 1)
        r3 = _intersect(c12, c21, (mid1, t1_max), (t2_min, mid2), i + 1)
        r4 = _intersect(c12, c22, (mid1, t1_max), (mid2, t2_max), i + 1)

        return r1 + r2 + r3 + r4

    return _intersect(curve1, curve2, curve1.get_u_bounds(),
                      curve2.get_u_bounds())