''' Created on 15/06/2018 @author: rjag008 ''' try: from PySide import QtCore from PySide.QtGui import QColor, QBrush, QPen signalHandle = QtCore.Signal except ImportError: #from PyQt4 import QtCore #from PyQt4.Qt import QColor, QBrush, QPen #signalHandle = QtCore.pyqtSignal pass from collections import OrderedDict from os import path import pickle import numpy as np class PaintModel(QtCore.QObject): # Signals modelChanged = signalHandle() def __init__(self): QtCore.QObject.__init__(self, None) self.reset() def reset(self): self._pointIndex = 0 self._backgroundFile = None self._points = OrderedDict() self.datasetColors = dict() self._datasets = OrderedDict() self._datasetKey = 0 self._currentDataset = None #Target axis self._axisRefs = {} self._mode = 0 #Current Axis self.x0 = 0.0 self.x1 = 1.0 self.y0 = 0.0 self.y1 = 1.0 def newDataSet(self, background_file = None): self.reset() if not background_file is None: self.addImage(background_file) self.addDataset() def saveDataset(self, projectFile): if len(self._points)==0: return False # Construct dict to dump d = {} if not self._backgroundFile is None: dir_name = path.dirname(str(projectFile)) rel_path = path.relpath(str(self._backgroundFile), str(dir_name)) d["background_file"] = str(rel_path) else: d["background_file"] = None d["x0"] = self.x0 d["x1"] = self.x1 d["y0"] = self.y0 d["y1"] = self.y1 # Store axis refs coordinates axis_refs = {} for key in self._axisRefs: if self._axisRefs[key] is None: axis_refs[key] = None else: axis_refs[key] = self._axisRefs[key] d["axisRefs"] = axis_refs # Store items coordinates points = [] for key in self._points: p, dataset = self._points[key] points.append([p, dataset]) d["points"] = points datasets = self._datasets datasetKey = self._datasetKey currentDataset = self._currentDataset datasetColors = dict() for k,v in self.datasetColors.items(): if isinstance(v[0],QPen): #Handle difference between data stored between QPainter and ZincDigitizer datasetColors[k] = [v[0].color().rgba()] else: crgba = v[1].rgba() datasetColors[k] = [v[0],crgba] d["datasets"] = datasets d["datasetKey"] = datasetKey d["currentDataset"] = currentDataset d['datasetColors'] = datasetColors try: with open(projectFile, "wb") as oF: pickle.dump("SPARCPaintV0.1", oF,2) pickle.dump(d, oF,2) return True except: return False def reassignIds(self,d): dsk = self._datasetKey kmap = dict() if "datasets" in d: ds = d['datasets'] newDataset = OrderedDict() for k,v in ds.items(): if k in self._datasets: kmap[k] = dsk newDataset[dsk] = v dsk +=1 else: kmap[k] = k newDataset[k] = v d['datasets'] = newDataset d['datasetKey'] = dsk if 'datasetColors' in d: dcolors = dict() ds = d['datasetColors'] for k,v in ds.items(): if not k in self.datasetColors: dcolors[k] = v d['datasetColors'] = ds newPoints = [] for data in d["points"]: if isinstance(data, list) or isinstance(data, tuple): p, dataset = data newPoints.append([p,kmap[dataset]]) else: newPoints.append(data) d['points'] = newPoints return d def loadDataset(self, projectFile): #self.reset() with open(projectFile, "rb") as iF: header = pickle.load(iF) if header == "SPARCPaintV0.1": d = pickle.load(iF) if len(self._datasets) > 0: #Renumber dataset to ensure it doesnt clash with existing keys d = self.reassignIds(d) def assignIfExists(var, d, keyname): if keyname in d: return d[keyname] else: return var background_file = d["background_file"] if header == "PaintV0.3" and not background_file is None: dir_name = path.dirname(str(projectFile)) background_file = path.normpath(path.join(dir_name, background_file)) self.addImage(background_file) self.x0 = assignIfExists(self.x0, d, "x0") self.x1 = assignIfExists(self.x1, d, "x1") self.y0 = assignIfExists(self.y0, d, "y0") self.y1 = assignIfExists(self.y1, d, "y1") self._axisRefs = {} for key in d["axisRefs"]: if not d["axisRefs"][key] is None: p = d["axisRefs"][key] if isinstance(p, QtCore.QPointF): p = [p.x(), p.y()] self._axisRefs[key]=p #self.datasetColors = dict() if 'datasetColors' in d: ds = d['datasetColors'] for k,v in ds.items(): if len(v)==2: color = QColor(v[1]) self.datasetColors[k] = [v[0],color] else: color = QColor(v[0]) brush = QBrush() bcolor = QColor(color) bcolor.setAlpha(127) brush.setColor(bcolor) brush.setStyle(QtCore.Qt.CrossPattern) pen = QPen(color, 1, QtCore.Qt.SolidLine,QtCore.Qt.FlatCap, QtCore.Qt.MiterJoin) self.datasetColors[k] = [pen,brush] if "datasets" in d: #self._datasets = d["datasets"] for k,v in d['datasets'].items(): self._datasets[k] = v self._datasetKey = d["datasetKey"] self._currentDataset = d["currentDataset"] else: self._datasets = OrderedDict({0: "Dataset 0"}) self._datasetKey = 1 self._currentDataset = 0 for data in d["points"]: p, dataset = [0,0], 0 if isinstance(data, list) or isinstance(data, tuple): p, dataset = data elif isinstance(data, QtCore.QPointF): p = [data.x(), data.y()] else: continue if dataset in self._datasets: self._currentDataset = dataset self.addPointPassive(p) self.modelChanged.emit() return True self.modelChanged.emit() return False def addImage(self, background_file): self._backgroundFile = str(background_file) def getBackgroundFile(self): return self._backgroundFile def getPoints(self): return self._points def addPointPassive(self, pos): self._points[self._pointIndex] = [pos, self._currentDataset] self._pointIndex += 1 def addPoint(self, pos): self._points[self._pointIndex] = [pos, self._currentDataset] self._pointIndex += 1 self.modelChanged.emit() def removePoint(self, key): del self._points[key] self.modelChanged.emit() def movePoint(self, key, pos): if not key in self._points: return #Update if it belongs to current dataset ds = self._points[key][1] if ds == self._currentDataset: self._points[key][0] = pos self.modelChanged.emit() def setInsertMode(self,mode): self._mode = mode def computeCoordinates(self, pos): if len(self._axisRefs.keys()) != 4: return pos coord = np.zeros(2) x0Pos = self._axisRefs[1] x1Pos = self._axisRefs[2] y0Pos = self._axisRefs[3] y1Pos = self._axisRefs[4] pos = np.array(pos) # Find projecting over x axis A0 = x1Pos- x0Pos A1 = y1Pos- y0Pos b = pos - x0Pos alpha = np.dot(A0,b)/np.linalg.norm(A0) b = pos - y0Pos beta = np.dot(A1,b)/np.linalg.norm(A1) #Go from reference axis lengths to preferred axis lengths given by (x1,y1)-(x0,y0) scalingx = (self.x1 - self.x0)/np.linalg.norm(A0) scalingy = (self.y1 - self.y0)/np.linalg.norm(A1) coord[0] = self.x0 + (self.x1 - self.x0)/np.linalg.norm(self.x1-self.x0) * alpha * scalingx coord[1] = self.y0 + (self.y1 - self.y0)/np.linalg.norm(self.y1-self.y0) * beta * scalingy return coord def getPointsPerDataset(self): pointsPerDataset = OrderedDict() for dataset in self._datasets: pointsPerDataset[dataset] = [] for key in self._points: p, dataset = self._points[key] pointsPerDataset[dataset].append(p) count = max(len(pointsPerDataset[dataset]) for dataset in self._datasets) return pointsPerDataset, count def getPointsForDataset(self,dataset): pointInDataset = [] for key in self._points: p, pdataset = self._points[key] if pdataset==dataset: pointInDataset.append(p) return pointInDataset def getDatasetInTransformedCoordinates(self): pointsPerDataset, _ = self.getPointsPerDataset() result = dict() for dataset,name in self._datasets.items(): resultArray = [] for i in range(len(pointsPerDataset[dataset])): p = pointsPerDataset[dataset][i] resultArray.append(self.computeCoordinates(p)) result[name] = resultArray return result def exportToCSV(self, filename): pointsPerDataset, count = self.getPointsPerDataset() with open(filename, "w") as oF: for i in range(count): for dataset in self._datasets: if i < len(pointsPerDataset[dataset]): p = pointsPerDataset[dataset][i] oF.write("%f, %f, " % tuple(self.computeCoordinates(p))) else: oF.write(", , ") oF.write("\n") def addDataset(self, name = None): if name is None: name = "Group %i" % self._datasetKey key = self._datasetKey self._datasets[key] = name self._currentDataset = key self._datasetKey += 1 self.modelChanged.emit() def changeDatasetName(self,key,name): self._datasets[key] = name def removeDataset(self, key): del self._datasets[key] # Delete dangling points for pkey in self._points.keys(): if self._points[pkey][1] == key: del self._points[pkey] if self._currentDataset == key: if len(self._datasets.keys()) == 0: # If the last dataset was removed, add a new one instead self.addDataset() self._currentDataset = self._datasets.keys()[-1] if len(self._datasets.keys()) == 0: # If the last dataset was removed, add a new one instead self.addDataset() self.modelChanged.emit() def changeCurrentDataset(self, key): self._currentDataset = key def getDatasets(self): return self._datasets def getCurrentDataset(self): return self._currentDataset def setToScaffoldMeshScaling(self,meshCoordinateScalingFactor): self.x0 = -0.5 self.y0 = -0.5 self.x1 = 0.5 #Mesh is bound to unit cube with centroid in the middle self.y1 = 0.5 factor = meshCoordinateScalingFactor/2.0 self._axisRefs[1] = np.array([-factor,0.0]) self._axisRefs[2] = np.array([factor,0.0]) self._axisRefs[3] = np.array([0.0,-factor]) self._axisRefs[4] = np.array([0.0,factor])