def add_terrain_point(self, position, normal, variant_index=-1): """Adds terrain point to node. :param position: position of terrain point :type position: tuple | Vector :param normal: normal of terrain point :type normal: tuple | Vector :param variant_index: index of variant from PIT file :type variant_index: int """ self.ensure_variant(variant_index) # if no position and normal interrupt, also global count remains the same if position is None and normal is None: return # ordering with insertion by position where closest terrain point # shall be first in the list and farthest terrain point the last i = 0 while i < len(self.__tp_per_variant[variant_index]): pos, normal = self.__tp_per_variant[variant_index][i] if get_distance(self.__position, position) < get_distance( self.__position, pos): break i += 1 self.__tp_per_variant[variant_index].insert(i, (position, normal)) Node.__global_tp_counter += 1
def add_terrain_point(self, position, normal, variant_index=-1): """Adds terrain point to node. :param position: position of terrain point :type position: tuple | Vector :param normal: normal of terrain point :type normal: tuple | Vector :param variant_index: index of variant from PIT file :type variant_index: int """ self.ensure_variant(variant_index) # if no position and normal interrupt, also global count remains the same if position is None and normal is None: return # ordering with insertion by position where closest terrain point # shall be first in the list and farthest terrain point the last i = 0 while i < len(self.__tp_per_variant[variant_index]): pos, normal = self.__tp_per_variant[variant_index][i] if get_distance(self.__position, position) < get_distance(self.__position, pos): break i += 1 self.__tp_per_variant[variant_index].insert(i, (position, normal)) Node.__global_tp_counter += 1
def get_closest_point(self, point, iterations=_PL_consts.CURVE_CLOSEST_POINT_ITER): """Get's closest point on the curve to given point. NOTE: length of the curve must be already calculated. :param point: point to which closest curve point will be calculated :type point: mathutils.Vector :param iterations: number of iterastion for halving algorithm :type iterations: int :return: curve position coeficient of the closest point (0.0 - 1.0) :rtype: float """ curve_p1, curve_t1 = self.get_start() curve_p2, curve_t2 = self.get_end() interval = (0, 0.5, 1) while iterations > 0: curr_p = _curve_utils.smooth_curve(curve_p1, curve_t1, curve_p2, curve_t2, interval[0]) p1_distance = _math_utils.get_distance(curr_p, point) curr_p = _curve_utils.smooth_curve(curve_p1, curve_t1, curve_p2, curve_t2, interval[2]) p3_distance = _math_utils.get_distance(curr_p, point) if p1_distance < p3_distance: interval = (interval[0], (interval[0] + interval[1]) / 2, interval[1]) else: interval = (interval[1], (interval[1] + interval[2]) / 2, interval[2]) iterations -= 1 return interval[1]
def get_intersection_radius(curve1, curve2, curve1_pos_coef, curve2_pos_coef, curve1_direction=1, curve2_direction=1): """Get needed radius for reaching safe point when moving on curves in desired direction. In forst case full radius is returned which is the point where curve has no ancestors/children anymore. :param curve1: first curve :type curve1: io_scs_tools.exp.pip.curve.Curve :param curve2: second curve :type curve2: io_scs_tools.exp.pip.curve.Curve :param curve1_pos_coef: position coeficient of first curve for intersection point :type curve1_pos_coef: float :param curve2_pos_coef: position coeficient of second curve for intersection point :type curve2_pos_coef: float :param curve1_direction: first curve scaning direction (forward scaning: 1; backward scaning: -1) :type curve1_direction: int :param curve2_direction: second curve scaning direction (forward scaning: 1; backward scaning: -1) :type curve2_direction: int :return: radius; 0 if curves have same fork/joint, depending on their direction :rtype: int """ steps = (0.1, 0.1) # size of the step on curve in each iteration curr_c = [curve1, curve2] # current curves curr_pos = [curr_c[0].get_length() * curve1_pos_coef, curr_c[1].get_length() * curve2_pos_coef] # current curves positions # first point and tangent vectors for both curves curve_p1 = [Vector(), Vector()] curve_t1 = [Vector(), Vector()] # second point and tangent vectors for both curves curve_p2 = [Vector(), Vector()] curve_t2 = [Vector(), Vector()] curve_directions = (curve1_direction, curve2_direction) # stepping directions for both curves # current radius and distance between curves radius = 0 distance = 0 # advance until distance is meet while distance <= _PL_consts.SAFE_DISTANCE: # get data for both curves for i in range(2): # go to next position on curve old_curr_pos = curr_pos[i] if curve_directions[i] == 1: curr_pos[i] = min(curr_pos[i] + steps[i], curr_c[i].get_length()) else: curr_pos[i] = max(0, curr_pos[i] - steps[i]) # if we reached end of the curve, try to get on next/previous one or exit if old_curr_pos == curr_pos[i]: # step out if no next/previous possible curve if len(curr_c[i].get_next_prev_curves(curve_directions[i] == 1)) < 1: return radius curr_c[i] = curr_c[i].get_next_prev_curves(curve_directions[i] == 1)[0] if curve_directions[i] == 1: curr_pos[i] = min(steps[i], curr_c[i].get_length()) else: curr_pos[i] = max(0, curr_c[i].get_length() - steps[i]) curve_p1[i], curve_t1[i] = curr_c[i].get_start() curve_p2[i], curve_t2[i] = curr_c[i].get_end() # extra check if curves have same fork/joint; # then calculated radius has to be ignored therefore return zero radius next_prev_c1 = curr_c[0].get_next_prev_curves(curve_directions[i] == 1) next_prev_c2 = curr_c[1].get_next_prev_curves(curve_directions[i] == 1) if len(next_prev_c1) == 1 and len(next_prev_c2) == 1: if next_prev_c1[0] == next_prev_c2[0]: return 0 # if everything is okay finally calculate curve points, distance and radius curr_p1 = _curve_utils.smooth_curve(curve_p1[0], curve_t1[0], curve_p2[0], curve_t2[0], curr_pos[0] / curr_c[0].get_length()) curr_p2 = _curve_utils.smooth_curve(curve_p1[1], curve_t1[1], curve_p2[1], curve_t2[1], curr_pos[1] / curr_c[1].get_length()) distance = _math_utils.get_distance(curr_p1, curr_p2) radius += steps[0] return radius
def get_intersection_radius(curve1, curve2, curve1_pos_coef, curve2_pos_coef, curve1_direction=1, curve2_direction=1): """Get needed radius for reaching safe point when moving on curves in desired direction. In forst case full radius is returned which is the point where curve has no ancestors/children anymore. :param curve1: first curve :type curve1: io_scs_tools.exp.pip.curve.Curve :param curve2: second curve :type curve2: io_scs_tools.exp.pip.curve.Curve :param curve1_pos_coef: position coeficient of first curve for intersection point :type curve1_pos_coef: float :param curve2_pos_coef: position coeficient of second curve for intersection point :type curve2_pos_coef: float :param curve1_direction: first curve scaning direction (forward scaning: 1; backward scaning: -1) :type curve1_direction: int :param curve2_direction: second curve scaning direction (forward scaning: 1; backward scaning: -1) :type curve2_direction: int :return: radius; 0 if curves have same fork/joint, depending on their direction :rtype: int """ steps = (0.1, 0.1) # size of the step on curve in each iteration curr_c = [curve1, curve2] # current curves curr_pos = [ curr_c[0].get_length() * curve1_pos_coef, curr_c[1].get_length() * curve2_pos_coef ] # current curves positions # first point and tangent vectors for both curves curve_p1 = [Vector(), Vector()] curve_t1 = [Vector(), Vector()] # second point and tangent vectors for both curves curve_p2 = [Vector(), Vector()] curve_t2 = [Vector(), Vector()] curve_directions = (curve1_direction, curve2_direction ) # stepping directions for both curves # current radius and distance between curves radius = 0 distance = 0 # advance until distance is meet while distance <= _PL_consts.SAFE_DISTANCE: # get data for both curves for i in range(2): # go to next position on curve old_curr_pos = curr_pos[i] if curve_directions[i] == 1: curr_pos[i] = min(curr_pos[i] + steps[i], curr_c[i].get_length()) else: curr_pos[i] = max(0, curr_pos[i] - steps[i]) # if we reached end of the curve, try to get on next/previous one or exit if old_curr_pos == curr_pos[i]: # step out if no next/previous possible curve if len(curr_c[i].get_next_prev_curves( curve_directions[i] == 1)) < 1: return radius curr_c[i] = curr_c[i].get_next_prev_curves( curve_directions[i] == 1)[0] if curve_directions[i] == 1: curr_pos[i] = min(steps[i], curr_c[i].get_length()) else: curr_pos[i] = max(0, curr_c[i].get_length() - steps[i]) curve_p1[i], curve_t1[i] = curr_c[i].get_start() curve_p2[i], curve_t2[i] = curr_c[i].get_end() # extra check if curves have same fork/joint; # then calculated radius has to be ignored therefore return zero radius next_prev_c1 = curr_c[0].get_next_prev_curves( curve_directions[i] == 1) next_prev_c2 = curr_c[1].get_next_prev_curves( curve_directions[i] == 1) if len(next_prev_c1) == 1 and len(next_prev_c2) == 1: if next_prev_c1[0] == next_prev_c2[0]: return 0 # if everything is okay finally calculate curve points, distance and radius curr_p1 = _curve_utils.smooth_curve( curve_p1[0], curve_t1[0], curve_p2[0], curve_t2[0], curr_pos[0] / curr_c[0].get_length()) curr_p2 = _curve_utils.smooth_curve( curve_p1[1], curve_t1[1], curve_p2[1], curve_t2[1], curr_pos[1] / curr_c[1].get_length()) distance = _math_utils.get_distance(curr_p1, curr_p2) radius += steps[0] return radius
def add_terrain_point(self, position, normal, variant_index=-1): """Adds terrain point to node. :param position: position of terrain point :type position: tuple | Vector :param normal: normal of terrain point :type normal: tuple | Vector :param variant_index: index of variant from PIT file :type variant_index: int """ self.ensure_variant(variant_index) # if no position and normal interrupt, also global count remains the same if position is None and normal is None: return # find nearest existing terrain point i = 0 smallest_dist = float("inf") smallest_dist_i = 0 tp_count = len(self.__tp_per_variant[variant_index]) while i < tp_count: pos, normal = self.__tp_per_variant[variant_index][i] curr_distance = get_distance(position, pos) if curr_distance < smallest_dist: smallest_dist = curr_distance smallest_dist_i = i i += 1 # depending on distance to closest, previous and next terrain point insert it so that points are sorted already if tp_count < 2: # no terrain points yet or just one just put it at the end insert_i = tp_count elif smallest_dist_i == 0: # the nearest is first just put it at start or behind the first one next_tp = self.__tp_per_variant[variant_index][smallest_dist_i + 1] closest_tp = self.__tp_per_variant[variant_index][smallest_dist_i] if get_distance(next_tp[0], position) < get_distance(closest_tp[0], next_tp[0]): insert_i = 1 else: insert_i = 0 elif smallest_dist_i == tp_count - 1: # last is the nearest put it at the back or before last one prev_tp = self.__tp_per_variant[variant_index][smallest_dist_i - 1] closest_tp = self.__tp_per_variant[variant_index][smallest_dist_i] if get_distance(prev_tp[0], position) < get_distance(closest_tp[0], prev_tp[0]): insert_i = smallest_dist_i else: insert_i = smallest_dist_i + 1 else: # now this is a tricky one: once nearest point is in the middle. # With that in mind take previous and next existing points and calculate to which new point is closer. # After that also compare distance of next/previous to closest which gives us final answer # either point should be inserted before or after closest point. prev_tp = self.__tp_per_variant[variant_index][smallest_dist_i - 1] closest_tp = self.__tp_per_variant[variant_index][smallest_dist_i] next_tp = self.__tp_per_variant[variant_index][smallest_dist_i + 1] prev_tp_dist = get_distance(prev_tp[0], position) next_tp_dist = get_distance(next_tp[0], position) if next_tp_dist < prev_tp_dist and next_tp_dist < get_distance(closest_tp[0], next_tp[0]): insert_i = smallest_dist_i + 1 elif prev_tp_dist > get_distance(closest_tp[0], prev_tp[0]): insert_i = smallest_dist_i + 1 else: insert_i = smallest_dist_i self.__tp_per_variant[variant_index].insert(insert_i, (position, normal)) Node.__global_tp_counter += 1