Exemple #1
0
    def forward(self, distance, share=True):
        #print 'taturtle.py: def forward'
        scaled_distance = distance * self._turtles.turtle_window.coord_scale

        old = self.get_xy()  #Projected Point
        old_3D = self.get_3Dpoint()  #Actual Point

        #xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR)
        #ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR)

        xcor = old_3D[0] + scaled_distance * self._direction[0]
        ycor = old_3D[1] + scaled_distance * self._direction[1]
        zcor = old_3D[2] + scaled_distance * self._direction[2]

        width = self._turtles.turtle_window.width
        height = self._turtles.turtle_window.height

        old_point = Point3D(old_3D[0], old_3D[1],
                            old_3D[2])  # Old point as Point3D object
        p = old_point.project(width, height, 512, 512)  # Projected Old Point
        new_x, new_y = p.x, p.y
        pair1 = [new_x, new_y]
        pos1 = self._turtles.screen_to_turtle_coordinates(pair1)
        '''
        for i, val in enumerate(old_3D):
            if (abs(val) < 0.0001):
                old_3D[i] = 0.
            old_3D[i] = round(old_3D[i], 2)
        self._points.append([old_3D[0], old_3D[1], old_3D[2]])
        if (self._pen_state):
            self._points_penstate.append(1)
        else:
            self._points_penstate.append(0)
        '''

        self._3Dx, self._3Dy, self._3Dz = xcor, ycor, zcor
        self.store_data()

        new_point = Point3D(xcor, ycor, zcor)  # New point as 3D object
        p = new_point.project(width, height, 512, 512)  # Projected New Point
        new_x, new_y = p.x, p.y
        pair2 = [new_x, new_y]
        pos2 = self._turtles.screen_to_turtle_coordinates(pair2)
        #print 'new = ', new_point.x, new_point.y, new_point.z

        self._draw_line(pos1, pos2, True)
        #self.move_turtle((xcor, ycor))
        self.move_turtle((pos2[0], pos2[1]))

        if self._turtles.turtle_window.sharing() and share:
            event = 'f|%s' % (data_to_string(
                [self._turtles.turtle_window.nick,
                 int(distance)]))
            self._turtles.turtle_window.send_event(event)
Exemple #2
0
def onclick(event):
    # from https://stackoverflow.com/questions/6748184/matplotlib-plot-surface-get-the-x-y-z-values-written-in-the-bottom-right-cor/9673338#9673338
    if select_allowed and event.inaxes == ax and event.button == 1:
        try:
            data_string = ax.format_coord(event.xdata, event.ydata)
        except:
            return
        coord_list_parsed = re.split(r'[xyz=,\s]\s*',
                                     data_string)  # contains empty strings
        coord_list = np.array(
            [float(s) for s in coord_list_parsed if len(s) > 0])
        coord_list[0] = round(coord_list[0] * 10) / 10
        coord_list[1] = round(coord_list[1] * 10) / 10
        mag = np.linalg.norm(coord_list)
        scaled_point = coord_list / mag
        x, y, z = scaled_point[0], scaled_point[1], scaled_point[2]
        if (x, y, z) == (0, 0, 1):
            print("Please don't pick the north pole")
            return


#        scaled_point.resize(1,3) # for extraction purposes
        print("x = %f, y = %f, z = %f" % (x, y, z))
        #        ax.view_init(elev=default_elev, azim = default_azim) # default values
        new_point = Point3D(x, y, z)
        points.add(new_point)
        ax.scatter(x, y, z, c='r', linewidth=3.0)
        fig.canvas.draw()
Exemple #3
0
def makeInfill(perimeters, infill, direction):
    """Returns a list of lists of points by either x or y coordinate from min to max"""
    # TODO: Support direction
    if (not perimeters):
        return None
    if (infill == 0):
        return None
    final = []
    allSegments = []
    maxY = perimeters[0][0].start.y
    minY = perimeters[0][0].start.y
    maxX = perimeters[0][0].start.x
    minX = perimeters[0][0].start.x
    for perimeter in perimeters:
        for segment in perimeter:
            maxY = max(maxY, max(segment.start.y, segment.end.y))
            minY = min(minY, min(segment.start.y, segment.end.y))
            maxX = max(maxX, max(segment.start.x, segment.end.x))
            minX = min(minX, min(segment.start.x, segment.end.x))
            allSegments.append(segment)

    #linear interpolation between 0-100% infill
    layerThickness = .4

    #increment = (1-infill)*((abs(maxY) - abs(minY))) + infill*layerThickness
    increment = layerThickness / infill

    if (increment < 0):
        raise Exception("increment is negative")
    #increment = 1

    scan = minY + increment

    while (scan <= maxY):
        scanLine = Segment(Point3D(minX, scan, 0), Point3D(maxX, scan, 0),
                           None)  # note: probably dangerous to have none
        lineHits = []
        for seggy in allSegments:
            intersect = scanLine.intersect2D(seggy)
            if intersect:
                lineHits.append(intersect)
        # final.append(list(deleteDuplicates(lineHits)))
        sortedLineHits = sorted(lineHits, key=lambda x: x.x)
        final.append(sortedLineHits)
        scan += increment
    return final
Exemple #4
0
def resizePerimeters(params, perimeters, brim=False, raft=False):
    """Resizes perimeters using params.thickness toward the inside of that perimeter
  using the perpIn(dicular) unit vector to each segment in the perimeter.

  If brim or raft is specified, this will generate a brim or raft (cool right?)
  Note: brim and raft not yet functional (less cool)
  """
    if brim:
        raise Exception("brim not yet supported")  # TODO
    if raft:
        raise Exception("raft not yet supported")  # TODO

    thickness = params.thickness / 2.0
    new_perimeters = []
    for perimeter in perimeters:
        new_seggies = []
        for seggy in perimeter:  # Make copies to populate
            new_seggies.append(Segment(None, None, seggy.perpIn))

        for i in xrange(len(perimeter)):  # Start point translation algorithm
            seggy1 = perimeter[i]
            seggy2 = perimeter[(i + 1) % len(perimeter)]
            new_seggy1 = new_seggies[i]
            new_seggy2 = new_seggies[(i + 1) % len(
                new_seggies)]  # len perimeter and new_seggies should be same
            # if seggy1.end != seggy2.start:
            # raise Exception("Found neighboring segments in perimeter which don't share a point")
            # print seggy1.end
            # print seggy2.start
            # Considering the float imperfections and the imperfections in perimeter handling
            # we ignore this error. We'll consider the end point of the first segment. If the
            # points printed are really different though, that indicates a bigger problem.
            point2d = [seggy1.end.x, seggy1.end.y]  # is the same as seggy2.y
            # Find unit vector of distance thickness from the origin in direction of perpendicular
            trans_end1 = map(lambda x: x * thickness, seggy1.perpIn)
            trans_start2 = map(lambda x: x * thickness, seggy2.perpIn)
            trans_amt = map(lambda x, y: x + y, trans_end1, trans_start2)
            new_point2d = map(lambda x, y: x + y, point2d, trans_amt)
            # Populate the new segments with translated point
            new_seggy1.end = Point3D(new_point2d[0], new_point2d[1], float(0))
            new_seggy2.start = Point3D(new_point2d[0], new_point2d[1],
                                       float(0))
        new_perimeters.append(new_seggies)
    return new_perimeters
Exemple #5
0
 def __init__(self, v1, v2, v3, norm):
     for x in [v1, v2, v3, norm]:
         try:
             n = len(x)
         except Exception as e:
             print(e)
             n = 0
         if n != 3:
             raise TypeError('Expected 3D vector.')
         for y in x:
             if not isinstance(y, numbers.Real):
                 raise TypeError('Expected 3D vector.')
     verts = [Point3D(v1), Point3D(v2), Point3D(v3)]
     # Re-order vertices in a normalized order.
     while verts[0] > verts[1] or verts[0] > verts[2]:
         verts = verts[1:] + verts[:1]
     self.vertices = verts
     self.norm = Vector(norm)
     self.count = 1
     self.fixup_normal()
Exemple #6
0
def compute_infinite_center(p1, p2, join_points):
    ''' given two 3d points p1, p2, return the circumcenter with (0,0,1)
    representing the endpoint of this edge toward infinity 
    uses join_points to determine which center to pick
    not guaranteed to be correct if p1, p2, (0,0,1), (0,0,0) are coplanar'''
    # mostly copied from above
    vec1 = np.array([p2.x - p1.x, p2.y - p1.y, p2.z - p1.z])
    vec2 = np.array([-p1.x, -p1.y, 1 - p1.z])
    normal = np.cross(vec1, vec2)
    mag = np.linalg.norm(normal)
    coords = normal / mag
    centerpoint = Point3D(coords[0], coords[1], coords[2])
    if centerpoint in join_points:
        #        print("join point found")
        #        print(centerpoint)
        return centerpoint


#    print("not found")
#    print(-coords[0], -coords[1], -coords[2])
    return Point3D(-coords[0], -coords[1], -coords[2])
Exemple #7
0
    def forward(self, distance, share=True):
        scaled_distance = distance * self._turtles.turtle_window.coord_scale

        old = self.get_xy() #Projected Point
        old_3D = self.get_3Dpoint() #Actual Point

        #xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR)
        #ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR)

        xcor = old_3D[0] + scaled_distance * self._direction[0]
        ycor = old_3D[1] + scaled_distance * self._direction[1]
        zcor = old_3D[2] + scaled_distance * self._direction[2]

        width = self._turtles.turtle_window.width
        height = self._turtles.turtle_window.height
        
        # Old point as Point3D object
        old_point = Point3D(old_3D[0], old_3D[1], old_3D[2])
        # Projected Old Point
        p = old_point.project(width, height, self._camera)
        new_x, new_y = p.x, p.y
        pair1 = [new_x, new_y]
        pos1 = self._turtles.screen_to_turtle_coordinates(pair1)
        
        self._3Dx, self._3Dy, self._3Dz = xcor, ycor, zcor
        self.store_data()

        new_point = Point3D(xcor, ycor, zcor) # New point as 3D object
        p = new_point.project(width, height, self._camera) # Projected New Point
        new_x, new_y = p.x, p.y
        pair2 = [new_x, new_y]
        pos2 = self._turtles.screen_to_turtle_coordinates(pair2)

        self._draw_line(pos1, pos2, True)
        self.move_turtle((pos2[0], pos2[1]))

        if self._turtles.turtle_window.sharing() and share:
            event = 'f|%s' % (data_to_string([self._turtles.turtle_window.nick,
                                              int(distance)]))
            self._turtles.turtle_window.send_event(event)
Exemple #8
0
    def set_xyz(self, x, y, z):
        ''' Set the x, y and z coordinates '''

        self._3Dx, self._3Dy, self._3Dz = x, y, z
        self.store_data()
        point_3D = Point3D(x, y, z)
        width = self._turtles.turtle_window.width
        height = self._turtles.turtle_window.height
        p = point_3D.project(width, height, 512, 512)
        new_x, new_y = p.x, p.y
        pair = [new_x, new_y]
        pos = self._turtles.screen_to_turtle_coordinates(pair)
        self.set_xy(pos[0], pos[1])
Exemple #9
0
    def draw_obj(self, file_name):

        vertices = []
        lines = []
        file_handle = open(file_name, 'r')

        for line in file_handle:
            temp = line.split()
            if temp[0] == 'v':
                vertices.append(
                    [float(temp[1]),
                     float(temp[2]),
                     float(temp[3])])
            if temp[0] == 'l':
                lines.append([int(temp[1]), int(temp[2])])

        width = self._turtles.turtle_window.width
        height = self._turtles.turtle_window.height

        for line in lines:
            source = vertices[line[0] - 1]
            dest = vertices[line[1] - 1]

            source_point = Point3D(source[0], source[1], source[2])
            p1 = source_point.project(width, height, 512, 512)
            pair1 = [p1.x, p1.y]
            pos1 = self._turtles.screen_to_turtle_coordinates(pair1)

            dest_point = Point3D(dest[0], dest[1], dest[2])
            p2 = dest_point.project(width, height, 512, 512)
            pair2 = [p2.x, p2.y]
            pos2 = self._turtles.screen_to_turtle_coordinates(pair2)

            self._draw_line(pos1, pos2, True)
            self.move_turtle((pos2[0], pos2[1]))

        return vertices, lines
Exemple #10
0
def compute_center(p1,
                   p2,
                   p3,
                   invert=None,
                   inverse_transform=None,
                   sphere_to_plane=None):
    ''' given three Point3Ds, return Point3D on sphere that is equidistant
    from all of them and whose image is inside the circumcircle
    of the inverted triangle 
    invert through (0,0,1) by default'''
    # compute normal to the plane using cross product to get equidistant line
    vec1 = np.array([p2.x - p1.x, p2.y - p1.y, p2.z - p1.z])
    vec2 = np.array([p3.x - p1.x, p3.y - p1.y, p3.z - p1.z])
    #    normal = np.cross(vec1, vec2).reshape((1,3))
    normal = np.cross(vec1, vec2)
    mag = np.linalg.norm(normal)
    coords = normal / mag  # one of the circumline points on the sphere
    #    print("NORMAL VECTOR")
    #    print(normal)
    centerpoint = Point3D(coords[0], coords[1], coords[2])
    # check orientation of center point
    if invert:
        inverted_center_arr = np.array(
            centerpoint.invert_through(invert)).reshape((3, 1))
        p_coords = (inverse_transform @ inverted_center_arr).reshape((3, ))
        p = Point(p_coords[0], p_coords[1])
        im_p1 = sphere_to_plane[p1]
        im_p2 = sphere_to_plane[p2]
        im_p3 = sphere_to_plane[p3]
    else:  # do the same for normal inversion
        p = centerpoint.invert()
        im_p1 = p1.invert()
        im_p2 = p2.invert()
        im_p3 = p3.invert()
    if is_in_circle(p, im_p1, im_p2, im_p3):
        return centerpoint
    return Point3D(-coords[0], -coords[1], -coords[2])
Exemple #11
0
    def init_ui(self):

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_expose)
        #self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self.add(self.darea)
        self.coords = []
        self.xy = []

        self.vert = [[1, 1, 1], [-1, 1, 1], [1, -1, 1], [1, 1, -1],
                     [-1, -1, 1], [-1, 1, -1], [1, -1, -1], [-1, -1, -1]]
        for i in self.vert:
            self.coords.append(Point3D(i[0], i[1], i[2]))

        #self.darea.connect("button-press-event", self.on_button_press)

        self.set_title("Lines")
        self.resize(640, 480)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
Exemple #12
0
    mag = np.linalg.norm(normal)
    coords = normal / mag
    centerpoint = Point3D(coords[0], coords[1], coords[2])
    if centerpoint in join_points:
        #        print("join point found")
        #        print(centerpoint)
        return centerpoint


#    print("not found")
#    print(-coords[0], -coords[1], -coords[2])
    return Point3D(-coords[0], -coords[1], -coords[2])

if __name__ == '__main__':
    #    point_set = {Point3D(0,1,0), Point3D(0,-1,0), Point3D(1,0,0), Point3D(-1,0,0)}
    #    VoronoiSphere(point_set)

    #    p = Point(0,1.5)
    #    p1 = Point(1,0)
    #    p2 = Point(0,1)
    #    p3 = Point(-1,0)
    #    print(is_in_circle(p, p1, p2, p3))

    #    p1 = Point3D(0,1,0)
    #    p2 = Point3D(-0.8,0.6,0)
    #    p3 = Point3D(0.64,0.48,0.6)
    #    print(compute_center(p1, p2, p3))
    p1 = Point3D(-0.726667, -0.121111, -0.676230)
    p2 = Point3D(-0.210467, -0.701558, 0.680823)
    print(compute_infinite_center(p1, p2))
Exemple #13
0
'''


def laplace(point1, point2, point3):
    try:
        vetor1 = point1.createVector(point2)
        vetor2 = point1.createVector(point3)

        x = vetor1.coordinates[1] * vetor2.coordinates[2] - vetor1.coordinates[
            2] * vetor2.coordinates[1]
        y = vetor1.coordinates[2] * vetor2.coordinates[0] - vetor1.coordinates[
            0] * vetor2.coordinates[2]
        z = vetor1.coordinates[0] * vetor2.coordinates[1] - vetor1.coordinates[
            1] * vetor2.coordinates[0]

        d = -(x * point1.point[0] + y * point1.point[1] + z * point1.point[2])

        print("Normal Vector: n({}, {}, {})".format(x, y, z))
        print("Plan Equation: {}x + {}y + {}z + {} = 0".format(x, y, z, d))
    except TypeError:
        print("The argument have to be of type Point3D")


'''
Main 
'''
if __name__ == '__main__':
    p1 = Point3D(5, 4, 3)
    p2 = Point3D(6, -1, 2)
    p3 = Point3D(7, 2, -4)
    laplace(p1, p2, p3)
Exemple #14
0
def test():
    """Because we're not using a real testing framework"""
    params = Parameters(filename="notused",
                        perimeterLayers=3,
                        infill=.20,
                        layerHeight=.19,
                        thickness=0.4,
                        support=False)

    # Test data from layer 0.19 of the 3mmBox
    perimeter = [
        Segment(Point3D(.19, 0.0, .19), Point3D(0.0, 0.0, .19), [0.0, 1.0]),
        Segment(Point3D(0.0, 0.0, 0.19), Point3D(0.0, 0.19, .19), [1.0, 0.0]),
        Segment(Point3D(0.0, .19, .19), Point3D(0.0, 15.0, .19), [1.0, 0.0]),
        Segment(Point3D(0.0, 15.0, .19), Point3D(14.810, 15.0, .19),
                [0.0, -1.0]),
        Segment(Point3D(14.810, 15.0, .19), Point3D(15.0, 15.0, .19),
                [0, -1.0]),
        Segment(Point3D(15.0, 15.0, .19), Point3D(15.0, 14.81, .19),
                [-1.0, 0.0]),
        Segment(Point3D(15.0, 14.81, .19), Point3D(15.0, 0.0, .19),
                [-1.0, 0.0]),
        Segment(Point3D(15.0, 0.0, .19), Point3D(0.19, 0.0, .19), [0.0, 1.0])
    ]
    perimeters = [perimeter]
    print "=============== testing perimeter resizing ==============="
    map(printFunc, resizePerimeters(params, perimeters)[0])

    print "=============== TEsting gcode Generation ==============="
    with open("test_gcode.gcode", "w") as gfile:
        generateSetup(gfile, params)
        generateGCode(gfile, params, 0.19, perimeters)
        generateCleanup(gfile)

    print "success"
Exemple #15
0
    # generate random points on unit sphere
    for i in range(n):
        point_selected = False
        while not point_selected:
            raw_x = random.uniform(-1, 1)
            raw_y = random.uniform(-1, 1)
            raw_z = random.uniform(-1, 1)
            magnitude = raw_x**2 + raw_y**2 + raw_z**2
            # scale to sphere
            x = raw_x / magnitude
            y = raw_y / magnitude
            z = raw_z / magnitude
            if (x, y, z) == (0, 0, 1):  # avoid north pole
                continue
            point_selected = True
        points.add(Point3D(x, y, z))

    assert len(points) == n

    # track time
    start_time = time.process_time()
    voronoi = VoronoiSphere(points, False)  # no printing
    while not voronoi.done():
        voronoi.step()
    output_info = voronoi.output()  # extra processing occurs during output

    # computation complete
    elapsed_time = time.process_time() - start_time

    print("Took %f seconds to compute on %d points" % (elapsed_time, n))
Exemple #16
0
 def find_far_section(self):
     ''' find the intersection of the second Voronoi diagram
     with the set of points closest to the image of (0,0,1)
     under inversion about eta '''
     voronoi_edges = {}  # hold final edge endpoints
     voronoi_sites = set()  # hold sites adjacent to self.q_inv
     join_points = set(
     )  # hold circumcenters for matching to near side points later
     # extract edges and vertices for voronoi region of self.q_inv
     edge_to_sets = self.voronoi_north.output()[1]
     for edge in edge_to_sets:
         plane_sites = edge.get_sites()
         if self.q_inv in plane_sites:
             for circle_set in edge_to_sets[edge]:
                 circle_list = list(circle_set)
                 circle_list.remove(self.q_inv)
                 for i in range(len(circle_list) - 1):
                     site1 = circle_list[i]
                     site2 = circle_list[i + 1]
                     # add to collection of sites being considered
                     voronoi_sites.add(site1)
                     voronoi_sites.add(site2)
                     # add this vertex to edges actually existing in the diagram
                     sphere_site1 = self.plane2_to_sphere[site1]
                     sphere_site2 = self.plane2_to_sphere[site2]
                     sphere_edge = Edge(sphere_site1, sphere_site2)
                     vertex = compute_center(sphere_site1, sphere_site2,
                                             Point3D(0, 0, 1), self.eta,
                                             self.inverse_transform,
                                             self.sphere_to_plane2)
                     join_points.add(vertex)
                     if sphere_edge in voronoi_edges:
                         voronoi_edges[sphere_edge].add(vertex)
                     else:
                         voronoi_edges[sphere_edge] = {vertex}
     # now voronoi_edges contains all points on the boundary of the far cap
     # get the portion of the Voronoi diagram inside the cap
     for edge in self.voronoi2.voronoi_vertices:
         site1, site2 = edge.get_sites()
         if site1 not in voronoi_sites or site2 not in voronoi_sites:
             continue  # only consider sites adjacent to q_inv
         # get points on the sphere
         sphere_site1 = self.plane2_to_sphere[site1]
         sphere_site2 = self.plane2_to_sphere[site2]
         sphere_edge = Edge(sphere_site1, sphere_site2)
         for circle_set in self.voronoi2.voronoi_vertices[edge]:
             circle_list = list(circle_set)[:3]
             intersection = compute_circumcenter(
                 circle_list[0], circle_list[1],
                 circle_list[2])  # a 2d point
             if intersection.distance(self.q_inv) <= intersection.distance(
                     site1):  # this point lies inside the region
                 sphere_points = [
                     self.plane2_to_sphere[point] for point in circle_list
                 ]
                 # convert directly to 3D coordinates
                 vertex = compute_center(sphere_points[0], sphere_points[1],
                                         sphere_points[2], self.eta,
                                         self.inverse_transform,
                                         self.sphere_to_plane2)
                 if sphere_edge in voronoi_edges:
                     voronoi_edges[sphere_edge].add(vertex)
                 else:
                     voronoi_edges[sphere_edge] = {vertex}
     return voronoi_edges, join_points
Exemple #17
0
    def __init__(self, point_set, verbose=True):
        ''' create a new VoronoiSphere object from given site points
        point_set is a set of Point3D objects representing the sites 
        verbose indicates printing verbosity'''
        self.verbose = verbose
        self.points = point_set
        # maps for inversion from sphere to plane and vice versa
        self.sphere_to_plane = {}
        self.plane_to_sphere = {}
        for point in self.points:
            inverse = point.invert()
            self.sphere_to_plane[point] = inverse
            self.plane_to_sphere[inverse] = point
        # also want to pick a second center of inversion inside the convex hull
        planar_iter = iter(self.plane_to_sphere)
        triangle = [next(planar_iter) for i in range(3)]
        while (are_collinear(triangle[0], triangle[1], triangle[2])):
            triangle[2] = next(planar_iter)
        # triangle will now be a valid non-degenerate triangle
        weight = 0
        while (True):
            eta_inv_x = 0.5 * triangle[0].get_x() + (0.25 - weight) * triangle[
                1].get_x() + (0.25 + weight) * triangle[2].get_x()
            eta_inv_y = 0.5 * triangle[0].get_y() + (0.25 - weight) * triangle[
                1].get_y() + (0.25 + weight) * triangle[2].get_y()
            eta_inv = Point(eta_inv_x, eta_inv_y)
            if eta_inv in self.plane_to_sphere or eta_inv == Point(
                    0, 0):  # this point exists
                weight = 1 / 8 if weight == 0 else weight / 2
            else:
                break  # good eta found
        a, b, c = eta_inv.project_to_sphere()
        self.eta = Point3D(a, b, c)  # second center of inversion

        # maps for inversion to second plane under coordinate transform,
        # where inverting is harder
        self.sphere_to_plane2 = {}
        self.plane2_to_sphere = {}
        # if x' are new coordinates, matrix_transform @ x' are old coords
        # new coordinates have z = -1 always
        z_dir = np.array([a, b, c])
        # obtain orthonormal basis for new coords
        if a == b and b == c:
            non_collinear = np.array([-b, c, -a])
        else:
            non_collinear = np.array([c, a, b])
        x_dir = np.cross(non_collinear, z_dir)
        y_dir = np.cross(z_dir, x_dir)
        matrix_transform = np.stack((x_dir, y_dir, z_dir), axis=-1)
        inverse_transform = np.linalg.inv(matrix_transform)
        self.inverse_transform = inverse_transform  # store for later
        # track the image of (0,0,1)
        q = np.array(Point3D(0, 0, 1).invert_through(self.eta)).reshape((3, 1))
        inverted_q = (inverse_transform @ q).reshape((3, ))
        # image of (0,0,1) under second inversion
        self.q_inv = Point(inverted_q[0], inverted_q[1])
        #        print(self.q_inv)
        self.sphere_to_plane2[Point3D(0, 0, 1)] = self.q_inv
        for point in self.points:  # these are 3d points
            inverted = np.array(point.invert_through(self.eta)).reshape((3, 1))
            new_coords = (inverse_transform @ inverted).reshape((3, ))
            inverted_point = Point(new_coords[0], new_coords[1])
            self.sphere_to_plane2[point] = inverted_point
            self.plane2_to_sphere[inverted_point] = point
#            print(point, inverted_point)

# construct the voronoi diagrams
        self.voronoi1 = Voronoi(set(self.plane_to_sphere.keys()), verbose)
        self.voronoi2 = Voronoi(set(self.plane2_to_sphere.keys()), verbose)
        self.voronoi_north = Voronoi(
            set(self.plane2_to_sphere.keys()).union({self.q_inv}), verbose)