Esempio n. 1
0
def test_closest_segment_and_distance_to_point_xy(example_model_and_crs):
    model, crs = example_model_and_crs
    c = np.array((123.45, 678.90, 0.0), dtype=float)
    nonagon = resqpy.lines.Polyline.for_regular_polygon(
        model, 9, 5.7, c, crs.uuid, 'nonagon')
    assert nonagon is not None
    assert len(nonagon.coordinates) == 9
    cp = np.mean(nonagon.coordinates, axis=0)
    assert_array_almost_equal(cp, c)
    for seg in range(9):
        p = np.array(nonagon.segment_midpoint(seg), dtype=float)
        m_seg, m_d = nonagon.closest_segment_and_distance_to_point_xy(p)
        assert m_seg is not None and m_d is not None
        assert m_seg == seg
        assert maths.isclose(m_d, 0.0, abs_tol=1.0e-6)
        p_in = (p - c) * 0.2 + c
        p_out = (p - c) * 23.4 + c
        for pp in [p_in, p_out]:
            d = vec.naive_length(p - pp)
            m_seg, m_d = nonagon.closest_segment_and_distance_to_point_xy(pp)
            assert m_seg is not None and m_d is not None
            assert m_seg == seg
            assert maths.isclose(m_d, d, rel_tol=1.0e-6)
        pv = nonagon.coordinates[seg]
        p_out = (pv - c) * 13.9 + c
        d = vec.naive_length(p_out - pv)
        m_seg, m_d = nonagon.closest_segment_and_distance_to_point_xy(p_out)
        assert m_seg is not None and m_d is not None
        assert m_seg in [seg, (seg - 1) % 9]
        assert maths.isclose(m_d, d, rel_tol=1.0e-6)
Esempio n. 2
0
File: _common.py Progetto: bp/resqpy
def _one_tangent(points, k1, k2, k3, weight):
    v1 = points[k2] - points[k1]
    v2 = points[k3] - points[k2]
    l1 = vu.naive_length(v1)
    l2 = vu.naive_length(v2)
    if weight == 'square':
        return vu.unit_vector(v1 / (l1 * l1) + v2 / (l2 * l2))
    elif weight == 'cube':
        return vu.unit_vector(v1 / (l1 * l1 * l1) + v2 / (l2 * l2 * l2))
    else:  # linear weight mode
        return vu.unit_vector(v1 / l1 + v2 / l2)
Esempio n. 3
0
    def balanced_centre(self, mode='weighted', n=20, cache=True, in_xy=False):
        """Returns a mean x,y,z based on sampling polyline at regular intervals."""

        if cache and self.centre is not None:
            return self.centre
        assert mode in ['weighted', 'sampled']
        if mode == 'sampled':  # this mode is deprecated as it simply approximates the weighted mode
            sample_points = self.equidistant_points(n, in_xy=in_xy)
            centre = np.mean(sample_points, axis=0)
        else:  # 'weighted'
            sum = np.zeros(3)
            seg_count = len(self.coordinates) - 1
            if self.isclosed:
                seg_count += 1
            d = 2 if in_xy else 3
            p1 = np.zeros(3)
            p2 = np.zeros(3)
            for seg_index in range(seg_count):
                successor = (seg_index + 1) % len(self.coordinates)
                p1[:d], p2[:d] = self.coordinates[
                    seg_index, :d], self.coordinates[successor, :d]
                sum += (p1 + p2) * vu.naive_length(p2 - p1)
            centre = sum / (2.0 * self.full_length(in_xy=in_xy))
        if cache:
            self.centre = centre
        return centre
Esempio n. 4
0
 def __shorter_sides_p_i(p3):
     max_length = -1.0
     max_i = None
     for i in range(3):
         opp_length = vec.naive_length(p3[i - 1] - p3[i - 2])
         if opp_length > max_length:
             max_length = opp_length
             max_i = i
     return max_i
Esempio n. 5
0
    def segment_length(self, segment_index, in_xy=False):
        """Returns the naive length (ie.

        assuming x,y & z units are the same) of an individual segment of the polyline.
        """

        successor = self._successor(segment_index)
        d = 2 if in_xy else 3
        return vu.naive_length(self.coordinates[successor, :d] -
                               self.coordinates[segment_index, :d])
Esempio n. 6
0
    def set_measured_depths(self):
        """Sets the measured depths from the start_md value and the control points."""

        self.measured_depths = np.empty(self.knot_count)
        self.measured_depths[0] = self.start_md
        for sk in range(1, self.knot_count):
            self.measured_depths[sk] = (
                self.measured_depths[sk - 1] +
                vec.naive_length(self.control_points[sk] -
                                 self.control_points[sk - 1]))
        self.finish_md = self.measured_depths[-1]

        return self.measured_depths
Esempio n. 7
0
 def df_trajectory(x, y, z):
     N = len(x)
     assert len(y) == N and len(z) == N
     df = pd.DataFrame(columns=['MD', 'X', 'Y', 'Z'])
     md = np.zeros(N)
     for n in range(N - 1):
         md[n + 1] = md[n] + vec.naive_length(
             (x[n + 1] - x[n], y[n + 1] - y[n], z[n + 1] - z[n]))
     df.MD = md
     df.X = x
     df.Y = y
     df.Z = z
     return df
Esempio n. 8
0
def interface_length(grid,
                     cell_kji0,
                     axis,
                     points_root=None,
                     cache_resqml_array=True,
                     cache_cp_array=False):
    """Returns the length between centres of an opposite pair of faces of the cell.

    note:
       assumes that x,y and z units are the same
    """

    assert cell_kji0 is not None
    return vec.naive_length(
        grid.interface_vector(cell_kji0,
                              axis,
                              points_root=points_root,
                              cache_resqml_array=cache_resqml_array,
                              cache_cp_array=cache_cp_array))
Esempio n. 9
0
File: _common.py Progetto: bp/resqpy
def spline(points,
           tangent_vectors=None,
           tangent_weight='square',
           min_subdivisions=1,
           max_segment_length=None,
           max_degrees_per_knot=5.0,
           closed=False):
    """Returns a numpy array containing resampled cubic spline of line defined by points.

    arguments:
        points (numpy float array of shape (N, 3)): points defining a line
        tangent_vectors (numpy float array of shape (N, 3), optional) if present, tangent
            vectors to use in the construction of the cubic spline; if None, tangents are
            calculated
        tangent_weight (string, default 'linear'): one of 'linear', 'square' or 'cube', giving
            increased weight to relatively shorter of 2 line segments at each knot when computing
            tangent vectors; ignored if tangent_vectors is not None
        min_subdivisions (int, default 1): the resulting line will have at least this number
            of segments per original line segment
        max_segment_length (float, optional): if present, resulting line segments will not
            exceed this length by much (see notes)
        max_degrees_per_knot (float, default 5.0): the change in direction at each resulting
            knot will not usually exceed this value (see notes)
        closed (boolean, default False): if True, the points are treated as a closed
            polyline with regard to end point tangents, otherwise as an open line

    returns:
        numpy float array of shape (>=N, 3) being knots on a cubic spline defined by points;
        original points are a subset of returned knots

    notes:
        the max_segment_length argument, if present, is compared with the length of each original
        segment to give a lower bound on the number of derived line segments; as the splined line
        may have extra curvature, the length of individual segments in the returned line can exceed
        the argument value, though usually not by much
        similarly, the max_degrees_per_knot is compared to the original deviations to provide another
        lower bound on the number of derived line segments for each original segment; as the spline
        may divide the segment unequally and also sometimes add loops, the resulting deviations can
        exceed the argument value
    """

    assert points.ndim == 2 and points.shape[1] == 3
    knot_count = len(points)
    assert knot_count > (2 if closed else 1)
    if tangent_vectors is None:
        assert tangent_weight in ['linear', 'square', 'cube']
        tangent_vectors = tangents(points,
                                   weight=tangent_weight,
                                   closed=closed)
    assert tangent_vectors.shape == points.shape
    assert min_subdivisions >= 1
    assert max_segment_length is None or max_segment_length > 0.0
    assert max_degrees_per_knot is None or max_degrees_per_knot > 0.0

    seg_count = knot_count if closed else knot_count - 1
    seg_lengths = np.empty(seg_count)
    for seg in range(knot_count - 1):
        seg_lengths[seg] = vu.naive_length(points[seg + 1] - points[seg])
    if closed:
        seg_lengths[-1] = vu.naive_length(points[0] - points[-1])

    knot_insertions = np.full(seg_count, min_subdivisions - 1, dtype=int)
    _prepare_knot_insertions(knot_insertions, max_segment_length, seg_count,
                             seg_lengths, max_degrees_per_knot, knot_count,
                             tangent_vectors, points)
    insertion_count = np.sum(knot_insertions)
    log.debug(f'{insertion_count} knot insertions for spline')
    spline_knot_count = knot_count + insertion_count

    spline_points = np.empty((spline_knot_count, 3))
    sk = 0
    for knot in range(seg_count):
        next = knot + 1 if knot < knot_count - 1 else 0
        spline_points[sk] = points[knot]
        sk += 1
        for i in range(knot_insertions[knot]):
            t = float(i + 1) / float(knot_insertions[knot] + 1)
            t2 = t * t
            t3 = t * t2
            p = np.zeros(3)
            p += (2.0 * t3 - 3.0 * t2 + 1.0) * points[knot]
            p += (t3 - 2.0 * t2 +
                  t) * tangent_vectors[knot] * seg_lengths[knot]
            p += (-2.0 * t3 + 3.0 * t2) * points[next]
            p += (t3 - t2) * tangent_vectors[next] * seg_lengths[knot]
            spline_points[sk] = p
            sk += 1
    if not closed:
        spline_points[sk] = points[-1]
        sk += 1
    assert sk == spline_knot_count

    return spline_points
Esempio n. 10
0
    def normalised_xy(self, x, y, mode='square'):
        """Returns a normalised x',y' pair (in range 0..1) being point x,y under mapping from convex polygon.

        arguments:
            x, y (floats): location of a point inside the polyline, which must be closed and project to a
                convex polygon in the xy plane
            mode (string): which mapping algorithm to use, one of: 'square', 'circle', or 'perimeter'

        returns:
            x', y' (floats, each in range 0..1) being the normalised representation of point x,y

        notes:
            this method is the inverse of denormalised_xy(), as long as a consistent mode is selected;
            for more details of the mapping used by the 3 modes, see documentation for denormalised_xy()
        """

        assert mode in ['square', 'perimeter', 'circle']
        assert self.is_convex(
        ), 'attempt to find normalised x,y within a polyline that is not a closed convex polygon'
        centre_xy = self.balanced_centre()[:2]
        if tuple(centre_xy) == (x, y):
            if mode == 'square':
                return 0.5, 0.5
            return 0.0, 0.0
        segment, px, py = self.first_line_intersection(centre_xy[0],
                                                       centre_xy[1],
                                                       x,
                                                       y,
                                                       half_segment=True)
        assert px is not None
        norm_x, norm_y = None, None
        if mode == 'square':  # todo: check square mode – looks wrong
            if px == centre_xy[0]:
                norm_x = 0.5
            else:
                norm_x = 0.5 + 0.5 * (x - centre_xy[0]) / abs(px -
                                                              centre_xy[0])
            if py == centre_xy[1]:
                norm_y = 0.5
            else:
                norm_y = 0.5 + 0.5 * (y - centre_xy[1]) / abs(py -
                                                              centre_xy[1])
        elif mode == 'circle':
            d_xy = np.array((x, y)) - centre_xy
            dp_xy = np.array((px, py)) - centre_xy
            if abs(dp_xy[0]) > abs(dp_xy[1]):
                rf = d_xy[0] / dp_xy[0]
                theta = maths.atan(d_xy[1] / d_xy[0])
                if d_xy[0] < 0.0:
                    theta += maths.pi
            else:
                rf = d_xy[1] / dp_xy[1]
                theta = maths.pi / 2.0 - maths.atan(d_xy[0] / d_xy[1])
                if d_xy[1] < 0.0:
                    theta += maths.pi
            norm_x = rf * rf
            theta /= 2.0 * maths.pi
            if theta < 0.0:
                theta += 1.0
            elif theta > 1.0:
                theta -= 1.0
            norm_y = theta
        elif mode == 'perimeter':
            d_xy = np.array((x, y)) - centre_xy
            dp_xy = np.array((px, py)) - centre_xy
            if abs(dp_xy[0]) > abs(dp_xy[1]):
                rf = d_xy[0] / dp_xy[0]
            else:
                rf = d_xy[1] / dp_xy[1]
            norm_x = rf * rf
            seg_xy = np.array((px, py)) - self.coordinates[segment, :2]
            length = 0.0
            for seg in range(segment):
                length += self.segment_length(seg, in_xy=True)
            length += vu.naive_length(seg_xy)
            norm_y = length / self.full_length(in_xy=True)
        return norm_x, norm_y