예제 #1
0
    def __init__(self):
        self._timer: utils.Timer = utils.Timer()
        self._application: application.Application = application.Application.instance(
        )

        self._source_np: Optional[np.ndarray] = None
        self._source_cp: Optional[cp.ndarray] = None
        self.offset: tuples.PixelPoint = tuples.PixelPoint(0, 0)

        # portal arrays that only change when window is resized
        self.frame_shape: Optional[tuples.ImageShape] = None
        self._pan: tuples.PixelPoint = tuples.PixelPoint(0, 0)
        self._rotation_degrees: float = 0.0
        self._scale: float = 1.0
        self._scale_point: tuples.PixelPoint = tuples.PixelPoint(0, 0)

        self._use_gpu: bool = True

        # numpy transformations
        self._matrix: Optional[np.ndarray] = None
        self._vector: Optional[np.ndarray] = None
        self._source_frame_matrix: Optional[np.ndarray] = None
        self._source_frame_vector: Optional[np.ndarray] = None

        # numpy and cupy frame array (both prepared in advance)
        self._frame_pixels_np: Optional[np.ndarray] = None
        self._frame_pixels_cp: Optional[cp.ndarray] = None

        # result (GPU or CPU)
        self._frame_rgba: Optional[np.ndarray] = None
예제 #2
0
 def total_pan(self) -> tuples.PixelPoint:
     if self.released_pan_delta is None:
         return tuples.PixelPoint(x=self.mouse_pan_delta.x,
                                  y=self.mouse_pan_delta.y)
     else:
         return tuples.PixelPoint(
             x=self.released_pan_delta.x + self.mouse_pan_delta.x,
             y=self.released_pan_delta.y + self.mouse_pan_delta.y)
예제 #3
0
 def _fill_boxes(self):
     rows, cols = self.box_value.shape
     for row in range(rows):
         for col in range(cols):
             if self.box_same[row, col]:
                 # define inside of box
                 bottom_left = tuples.PixelPoint(x=col * self.mesh_step + 1,
                                                 y=row * self.mesh_step + 1)
                 top_right = tuples.PixelPoint(
                     x=(col + 1) * self.mesh_step - 1,
                     y=(row + 1) * self.mesh_step - 1)
                 self.server.fill_box_request(bottom_left, top_right,
                                              self.box_value[row, col])
예제 #4
0
 def source_to_transformed_frame(self, source_point: tuples.PixelPoint):
     # always uses numpy as just one point
     source_np = np.array([source_point.x, source_point.y], dtype=float)
     frame_np = np.matmul(self._source_frame_matrix,
                          source_np) + self._source_frame_vector
     frame_point = tuples.PixelPoint(x=frame_np[0], y=frame_np[1])
     return frame_point
예제 #5
0
 def center_frame_point(self) -> Optional[tuples.ImageShape]:
     frame_shape = self.frame_shape
     if frame_shape is None:
         return None
     else:
         center_frame_point = tuples.PixelPoint(
             x=(self.frame_shape.x - 1) * 0.5,
             y=(self.frame_shape.y - 1) * 0.5)
         return center_frame_point
예제 #6
0
 def get_complex_from_frame_point(
         self, frame_shape: tuples.ImageShape,
         frame_point: tuples.PixelPoint) -> complex:
     # print(f"frame_shape: {frame_shape}")
     # print(f"frame_point: {frame_point}")
     # print(f"shape: {self.shape}")
     center_diff = tuples.PixelPoint(
         x=frame_point.x - 0.5 * (frame_shape.x - 1),
         y=frame_point.y - 0.5 * (frame_shape.y - 1))
     return self.get_complex_from_center_diff(center_diff)
예제 #7
0
 def _calc_offset(
         self,
         previous_shape: tuples.ImageShape,
         new_shape: tuples.ImageShape,
         pan: Optional[tuples.PixelPoint] = None) -> tuples.PixelPoint:
     # possibly should be floats, especially if pan is None
     x = int((previous_shape.x - new_shape.x) / 2.0)
     y = int((previous_shape.y - new_shape.y) / 2.0)
     if pan is not None:
         x += pan.x
         y += pan.y
     return tuples.PixelPoint(x, y)
예제 #8
0
    def _update_offset(self):
        """Called once canvas or frame is updated"""
        canvas_shape = self._canvas_source.shape
        frame_shape = self._frame.frame_shape
        # print(f"canvas_shape: {canvas_shape}")
        # print(f"frame_shape: {frame_shape}")
        if canvas_shape is not None and frame_shape is not None:
            current_frame_offset = self._frame.offset
            # offset when frame centered in canvas
            offset = tuples.PixelPoint(
                x=int((canvas_shape.x - frame_shape.x) / 2.0),
                y=int((canvas_shape.y - frame_shape.y) / 2.0))

            # print(f"offset: {offset}")
            if current_frame_offset is None or offset != current_frame_offset:
                self._frame.set_offset(offset)
예제 #9
0
    def add_border(self, mandel: mandelbrot.Mandel):
        border_size = 14 * 4 * 10  # add 5 large boxes in all directions
        current_shape = mandel.shape
        new_shape = tuples.ImageShape(current_shape.x + border_size * 2,
                                      current_shape.y + border_size * 2)
        self.new_mandel = mandel.lite_copy(shape=new_shape, has_border=True)

        offset = tuples.PixelPoint(x=-border_size, y=-border_size)

        job = mandelbrot.MandelJob(
            compute_manager=self._compute_manager,
            new_mandel=self.new_mandel,
            prev_mandel=mandel,
            offset=offset,
            display_progress=False,  # background job
            save_history=False)
        self._calc_thread_manager.request_job(job,
                                              queue_as=thread.QueueAs.ENQUEUE)
예제 #10
0
    def get_source_point_from_complex(
            self, z: complex) -> Optional[tuples.PixelPoint]:
        dz = z - self.centre

        # take dot products
        x_dist = dz.real * self.x_unit.real + dz.imag * self.x_unit.imag
        y_dist = dz.real * self.y_unit.real + dz.imag * self.y_unit.imag

        x_pixels_from_center = x_dist / self.size_per_gap
        y_pixels_from_center = y_dist / self.size_per_gap

        x_pixel = 0.5 * float(self.shape.x - 1) + x_pixels_from_center
        y_pixel = 0.5 * float(self.shape.y - 1) + y_pixels_from_center

        x = round(x_pixel)
        y = round(y_pixel)

        if 0 <= x <= self.shape.x - 1 and 0 <= y <= self.shape.y - 1:
            return tuples.PixelPoint(x, y)
        else:
            return None
예제 #11
0
 def top_right(self) -> tuples.PixelPoint:
     return tuples.PixelPoint(x=self.x_max, y=self.y_min)
예제 #12
0
 def bottom_left(self) -> tuples.PixelPoint:
     return tuples.PixelPoint(x=self.x_min, y=self.y_min)
예제 #13
0
 def mouse_pan_delta(self) -> Optional[tuples.PixelPoint]:
     if self.pan_start is not None and self.pan_end is not None:
         return tuples.PixelPoint(x=self.pan_start.x - self.pan_end.x,
                                  y=self.pan_start.y - self.pan_end.y)
     else:
         return tuples.PixelPoint(0, 0)
예제 #14
0
 def _reset_transform(self):
     self._pan = tuples.PixelPoint(0, 0)
     self._rotation_degrees = 0
     self._scale = 1.0
     self._scale_point = tuples.PixelPoint(0, 0)
예제 #15
0
    def _calculate_transform(self):
        """takes 0.0001s"""
        # purely numpy

        # functions
        identity = transform.Transform.identity
        flip_y = transform.Transform.flip_y
        translate = transform.Transform.translate
        scale = transform.Transform.scale
        rotate_degrees = transform.Transform.rotate_degrees

        # values
        # source_x = float(self._source.shape[1])
        source_y = float(self._source_np.shape[0])
        frame_x = float(self.frame_shape.x)
        frame_y = float(self.frame_shape.y)
        # cartesian offset when source and portal were first generated, x and y are both positive
        offset_x = float(self.offset.x)
        offset_y = float(self.offset.y)

        # goal is to get from frame_image co-ordinates to transformed source_image co-ordinates
        # want to do all the transforms in portal cartesian co-ordinates
        # path portal-image -> portal-cartesian -> portal-cartesian-transformed -> source-cartesian -> source-image

        # can't directly chain transforms.Affine2D so just deal in matrices

        # x unchanged, flip y (inverse of itself)

        frame_image_to_frame_cartesian = flip_y(frame_y)

        if self._rotation_degrees != 0.0:
            center_x, centre_y = frame_x * 0.5, frame_y * 0.5
            # 1) move center to origin
            # 2) rotate
            # 3) move origin back to center
            # so in reserve order:
            frame_transform = translate(center_x, centre_y) @ \
                rotate_degrees(self._rotation_degrees) @ \
                translate(-center_x, -centre_y)

        elif self._scale != 1.0:
            center_x, centre_y = frame_x * 0.5, frame_y * 0.5
            # 1) move scale_point to origin
            # 2) scale
            # 3) move origin back to center
            # so in reserve order:
            frame_transform = translate(self._scale_point.x, self._scale_point.y) @ \
                scale(self._scale) @ \
                translate(-center_x, -centre_y)

        elif self._pan != tuples.PixelPoint(0, 0):
            frame_transform = translate(self._pan.x, self._pan.y)

        else:
            frame_transform = identity()

        frame_cartesian_to_source_cartesian = translate(offset_x, offset_y)

        # x unchanged, flip y (inverse of itself)
        source_cartesian_to_source_image = flip_y(source_y)

        # combine matrices using multiplication, so in reserve order:
        frame_image_to_source_image = source_cartesian_to_source_image @ \
            frame_cartesian_to_source_cartesian @ \
            frame_transform @ \
            frame_image_to_frame_cartesian

        # final transform for frame_image to source_image
        self._matrix = frame_image_to_source_image[0:2, 0:2]
        self._vector = frame_image_to_source_image[0:2, 2]

        # source_point to frame_point transform
        source_cartesian_to_frame_cartesian = translate(-offset_x, -offset_y)
        inv_frame_transform = np.linalg.inv(frame_transform)
        # combine matrices using multiplication, so in reserve order:
        source_cartesian_to_frame_point = inv_frame_transform @ \
            source_cartesian_to_frame_cartesian
        self._source_frame_matrix = source_cartesian_to_frame_point[0:2, 0:2]
        self._source_frame_vector = source_cartesian_to_frame_point[0:2, 2]
예제 #16
0
 def _frame_image_to_frame_cartesian(self, x: int,
                                     y: int) -> tuples.PixelPoint:
     frame_y = self._view_state.frame_shape.y
     frame_point = tuples.PixelPoint(x=x, y=frame_y - y)
     return frame_point
예제 #17
0
 def on_resized(self, new_image_shape: tuples.ImageShape) -> tuples.ImageShape:
     self.clear_graph()
     min_length = min(new_image_shape.x, new_image_shape.y)
     self._image_shape = tuples.PixelPoint(x=min_length, y=min_length)
     return self._image_shape
예제 #18
0
 def run(self) -> np.ndarray:
     bottom_left = tuples.PixelPoint(0, 0)
     top_right = tuples.PixelPoint(x=self.shape.x, y=self.shape.y)
     self.server.box_request(bottom_left, top_right)
     self.server.serve()
     return self.server.iteration_cpu