Source code for sivicncdriver.ui.view3d

import matplotlib

matplotlib.use('Qt5Agg')
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Line3DCollection as LineCollection


from sivicncdriver import settings
from sivicncdriver.settings import logger
from sivicncdriver.gcode.gcode import parse
from sivicncdriver.gcode.arc_calculator import arc_to_segments


import matplotlib.pyplot as pl

_translate = QCoreApplication.translate


[docs]class View3D(FigureCanvasQTAgg): """ Prints G-Codes in 3D. """ parse_error = pyqtSignal(int) def __init__(self, parent=None, width=5, height=4, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) self.axes = fig.add_subplot(111, projection='3d') super(View3D, self).__init__(fig) self.setParent(parent) self.axes.mouse_init() super(View3D, self).setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) super(View3D, self).updateGeometry() self.segments_x = [] self.segments_y = [] self.segments_z = [] self.lines = {} self.data = []
[docs] def get_bounds(self): """ Returns the maximum and the minimum value on each axis. """ return { 'min_x': min(sum(self.segments_x, []), default=0), 'max_x': max(sum(self.segments_x, []), default=0), 'min_y': min(sum(self.segments_y, []), default=0), 'max_y': max(sum(self.segments_y, []), default=0), 'min_z': min(sum(self.segments_z, []), default=0), 'max_z': max(sum(self.segments_z, []), default=0) }
[docs] def compute_data(self, gcode): """ Computes the paths generated by a gcode file. :param gcode: The gcode. :type gcode: str """ current_pos = [0, 0, 0] self.segments_x = [] self.segments_y = [] self.segments_z = [] segment_no = 0 for t in parse(gcode): if t['name'] == '__error__': self.parse_error.emit(t['line']) break if t['name'] is not 'G': continue current_line = t['line'] x, y, z = current_pos x = t['args'].get('X', x) y = t['args'].get('Y', y) z = t['args'].get('Z', z) x_o, y_o, z_o = current_pos if t['value'] in (0, 1): self.segments_x.append([x_o, x]) self.segments_y.append([y_o, y]) self.segments_z.append([z_o, z]) self.lines[segment_no] = t['line'] segment_no += 1 elif t['value'] in (2, 3): i = t['args'].get('I', 0) j = t['args'].get('J', 0) clockwise = (t['value'] == 2) nb_segments = 0 segments = arc_to_segments( (x_o, y_o), (i, j), (x, y), clockwise ) for x_c, y_c in segments: self.segments_x.append([x_o, x_c]) self.segments_y.append([y_o, y_c]) x_o, y_o = x_c, y_c nb_segments += 1 points_z = np.linspace(z_o, z, nb_segments+1) for z_count in range(nb_segments): self.segments_z.append( [points_z[z_count], points_z[z_count+1]]) self.lines[segment_no] = t['line'] segment_no += 1 current_pos = x, y, z
[docs] def draw(self, **kwargs): """ :param reverse_x: Should the x axis be reversed ? (default : False) :param reverse_y: Should the y axis be reversed ? (default : False) :param reverse_z: Should the z axis be reversed ? (default : False) :param highlight_line: A line which is to be highlighted. (default : None) :type highlight_line: int :type reverse_x: bool :type reverse_y: bool :type reverse_z: bool """ self.axes.clear() self.axes.set_xlabel(_translate('View3D', 'X Axis')) self.axes.set_ylabel(_translate('View3D', 'Y Axis')) self.axes.set_zlabel(_translate('View3D', 'Z Axis')) reverse_x = kwargs.get('reverse_x', False) reverse_y = kwargs.get('reverse_y', False) reverse_z = kwargs.get('reverse_z', False) def reverse_func(x, y, z): if reverse_x: x *= -1 if reverse_y: y *= -1 if reverse_z: z *= -1 return (x, y, z) highlight_line = kwargs.get('highlight_line', None) bounds = self.get_bounds() min_z = bounds['min_z'] * (1 if not reverse_z else -1) max_z = bounds['max_z'] * (1 if not reverse_z else -1) map_z_to_ratio = lambda z: (z - min_z) / \ (max_z - min_z) if min_z != max_z else 0 map_z_to_color = lambda z: ( 1-map_z_to_ratio(z), 0.5, map_z_to_ratio(z)) segments = [] for x, y, z in zip(self.segments_x, self.segments_y, self.segments_z): segments.append(( reverse_func(x[0], y[0], z[0]), reverse_func(x[1], y[1], z[1]) )) colors = [] for l, p in enumerate(segments): if self.lines[l] == highlight_line: colors.append((0, 1, 0)) else: colors.append(map_z_to_color((p[0][2]+p[1][2])/2)) lines = LineCollection(segments, linewidths=1, linestyles='solid', colors=colors ) self.axes.add_collection3d(lines) if reverse_x: self.axes.invert_xaxis() if reverse_y: self.axes.invert_yaxis() if reverse_z: self.axes.invert_zaxis() extents = np.array([ [bounds['min_x'], bounds['max_x']], [bounds['min_y'], bounds['max_y']], [bounds['min_z'], bounds['max_z']], ]) sz = extents[:, 1] - extents[:, 0] centers = np.mean(extents, axis=1) maxsize = max(abs(sz)) r = maxsize/2 for ctr, dim in zip(centers, 'xyz'): getattr(self.axes, 'set_{}lim'.format(dim))(ctr - r, ctr + r) super(View3D, self).draw()