| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- ############################################################
- # FlatCAM: 2D Post-processing for Manufacturing #
- # http://caram.cl/software/flatcam #
- # Author: Juan Pablo Caram (c) #
- # Date: 2/5/2014 #
- # MIT Licence #
- ############################################################
- from PyQt5 import QtCore
- import logging
- from VisPyCanvas import VisPyCanvas
- from VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
- from vispy.scene.visuals import InfiniteLine, Line
- import numpy as np
- from vispy.geometry import Rect
- import time
- log = logging.getLogger('base')
- class PlotCanvas(QtCore.QObject):
- """
- Class handling the plotting area in the application.
- """
- def __init__(self, container, app):
- """
- The constructor configures the VisPy figure that
- will contain all plots, creates the base axes and connects
- events to the plotting area.
- :param container: The parent container in which to draw plots.
- :rtype: PlotCanvas
- """
- super(PlotCanvas, self).__init__()
- self.app = app
- # Parent container
- self.container = container
- # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
- # which might decrease performance
- self.b_line, self.r_line, self.t_line, self.l_line = None, None, None, None
- # Attach to parent
- self.vispy_canvas = VisPyCanvas()
- self.vispy_canvas.create_native()
- self.vispy_canvas.native.setParent(self.app.ui)
- self.container.addWidget(self.vispy_canvas.native)
- ### AXIS ###
- self.v_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=True,
- parent=self.vispy_canvas.view.scene)
- self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=False,
- parent=self.vispy_canvas.view.scene)
- # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
- # all CNC have a limited workspace
- self.draw_workspace()
- # if self.app.defaults['global_workspace'] is True:
- # if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
- # self.wkspace_t = Line(pos=)
- self.shape_collections = []
- self.shape_collection = self.new_shape_collection()
- self.app.pool_recreated.connect(self.on_pool_recreated)
- self.text_collection = self.new_text_collection()
- # TODO: Should be setting to show/hide CNC job annotations (global or per object)
- self.text_collection.enabled = False
- # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
- # all CNC have a limited workspace
- def draw_workspace(self):
- a = np.empty((0, 0))
- a4p_in = np.array([(0, 0), (8.3, 0), (8.3, 11.7), (0, 11.7)])
- a4l_in = np.array([(0, 0), (11.7, 0), (11.7, 8.3), (0, 8.3)])
- a3p_in = np.array([(0, 0), (11.7, 0), (11.7, 16.5), (0, 16.5)])
- a3l_in = np.array([(0, 0), (16.5, 0), (16.5, 11.7), (0, 11.7)])
- a4p_mm = np.array([(0, 0), (210, 0), (210, 297), (0, 297)])
- a4l_mm = np.array([(0, 0), (297, 0), (297,210), (0, 210)])
- a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
- a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
- if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
- if self.app.defaults['global_workspaceT'] == 'A4P':
- a = a4p_mm
- elif self.app.defaults['global_workspaceT'] == 'A4L':
- a = a4l_mm
- elif self.app.defaults['global_workspaceT'] == 'A3P':
- a = a3p_mm
- elif self.app.defaults['global_workspaceT'] == 'A3L':
- a = a3l_mm
- else:
- if self.app.defaults['global_workspaceT'] == 'A4P':
- a = a4p_in
- elif self.app.defaults['global_workspaceT'] == 'A4L':
- a = a4l_in
- elif self.app.defaults['global_workspaceT'] == 'A3P':
- a = a3p_in
- elif self.app.defaults['global_workspaceT'] == 'A3L':
- a = a3l_in
- self.delete_workspace()
- self.b_line = Line(pos=a[0:2], color=(0.70, 0.3, 0.3, 1.0),
- antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
- self.r_line = Line(pos=a[1:3], color=(0.70, 0.3, 0.3, 1.0),
- antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
- self.t_line = Line(pos=a[2:4], color=(0.70, 0.3, 0.3, 1.0),
- antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
- self.l_line = Line(pos=np.array((a[0], a[3])), color=(0.70, 0.3, 0.3, 1.0),
- antialias= True, method='agg', parent=self.vispy_canvas.view.scene)
- if self.app.defaults['global_workspace'] is False:
- self.delete_workspace()
- # delete the workspace lines from the plot by removing the parent
- def delete_workspace(self):
- try:
- self.b_line.parent = None
- self.r_line.parent = None
- self.t_line.parent = None
- self.l_line.parent = None
- except:
- pass
- # redraw the workspace lines on the plot by readding them to the parent view.scene
- def restore_workspace(self):
- try:
- self.b_line.parent = self.vispy_canvas.view.scene
- self.r_line.parent = self.vispy_canvas.view.scene
- self.t_line.parent = self.vispy_canvas.view.scene
- self.l_line.parent = self.vispy_canvas.view.scene
- except:
- pass
- def vis_connect(self, event_name, callback):
- return getattr(self.vispy_canvas.events, event_name).connect(callback)
- def vis_disconnect(self, event_name, callback):
- getattr(self.vispy_canvas.events, event_name).disconnect(callback)
- def zoom(self, factor, center=None):
- """
- Zooms the plot by factor around a given
- center point. Takes care of re-drawing.
- :param factor: Number by which to scale the plot.
- :type factor: float
- :param center: Coordinates [x, y] of the point around which to scale the plot.
- :type center: list
- :return: None
- """
- self.vispy_canvas.view.camera.zoom(factor, center)
- def new_shape_group(self):
- return ShapeGroup(self.shape_collection)
- def new_shape_collection(self, **kwargs):
- # sc = ShapeCollection(parent=self.vispy_canvas.view.scene, pool=self.app.pool, **kwargs)
- # self.shape_collections.append(sc)
- # return sc
- return ShapeCollection(parent=self.vispy_canvas.view.scene, pool=self.app.pool, **kwargs)
- def new_cursor(self):
- c = Cursor(pos=np.empty((0, 2)), parent=self.vispy_canvas.view.scene)
- c.antialias = 0
- return c
- def new_text_group(self):
- return TextGroup(self.text_collection)
- def new_text_collection(self, **kwargs):
- return TextCollection(parent=self.vispy_canvas.view.scene, **kwargs)
- def fit_view(self, rect=None):
- # Lock updates in other threads
- self.shape_collection.lock_updates()
- if not rect:
- rect = Rect(-1, -1, 20, 20)
- try:
- rect.left, rect.right = self.shape_collection.bounds(axis=0)
- rect.bottom, rect.top = self.shape_collection.bounds(axis=1)
- except TypeError:
- pass
- self.vispy_canvas.view.camera.rect = rect
- self.shape_collection.unlock_updates()
- def fit_center(self, loc, rect=None):
- # Lock updates in other threads
- self.shape_collection.lock_updates()
- if not rect:
- try:
- rect = Rect(loc[0]-20, loc[1]-20, 40, 40)
- except TypeError:
- pass
- self.vispy_canvas.view.camera.rect = rect
- self.shape_collection.unlock_updates()
- def clear(self):
- pass
- def redraw(self):
- self.shape_collection.redraw([])
- self.text_collection.redraw()
- def on_pool_recreated(self, pool):
- self.shape_collection.pool = pool
|