0 ベクタパス作成ツール 5
190721
ポイントの挿入と削除
- 出来た(・∀・)ノ
import sys , os , pickle
from PyQt5.QtWidgets import QApplication, QWidget,QMessageBox,QMenu,QAction
from PyQt5.QtCore import QPointF ,QRectF ,Qt,QDataStream,QIODevice,QDataStream,QFile
from PyQt5.QtGui import QPainterPath ,QPainter ,QPen,QCursor
class mypath(QPainterPath):
def __init__(self,p):
super().__init__(p)
self.pointrect=[]
self.a = QPointF(4,4) # Tolerance in detecting point
self.closed=0
def makepointrect(self): # ポイント変更の都度呼びたいけど
self.pointrect.clear()
for i in range(self.elementCount() ): # 0か2の前の3 or 0か二つ目の3
self.pointrect.append( [ i , QRectF( QPointF( self.elementAt(i).x,self.elementAt(i).y) -self.a ,
QPointF( self.elementAt(i).x,self.elementAt(i).y) + self.a) ] )
def setclosed(self,flag):
self.closed=flag
def index(self,p):
max=self.elementCount()
if self.closed :
if p==-1:
return max-2
elif p==max:
return max-1
else :
return p
else :
if p < 0 :
return 0
elif p > max -1 :
return max -1
else :
return p
def movepoint(self, p , pos , flag ): # flag : true-->sync
if flag :
lam = lambda q: QPointF(self.elementAt(q).x , self.elementAt(q).y )
type1,type2 = self.elementAt(p).type ,self.elementAt(self.index(p-1)).type
if type1==2 : # cp symmetry first
self.movepoint( self.index(p-2) , lam(self.index(p-1)) + lam(self.index(p-1)) - pos ,0) # (p-1) + pos->(p-1)
elif type1==3 and type2==2 : # cp symmetry second
self.movepoint( self.index(p+2) , lam(self.index(p+1)) + lam(self.index(p+1)) - pos ,0) # (p+1) + pos->(p+1)
else : # point parerell
self.movepoint( self.index(p+1) , lam(self.index(p+1)) - lam(p) + pos , 0) # (p+1) + p->pos
self.movepoint( self.index(p-1) , lam(self.index(p-1)) - lam(p) + pos , 0) # (p-1) + p->pos
if self.closed:
if p==0:
self.setElementPositionAt( self.elementCount()-1 , pos.x() , pos.y() )
self.setElementPositionAt( p , pos.x() , pos.y() )
#####ここにrect計算を書く
self.makepointrect()
def delpoint(self,p):
if p <0 :
print('select point')
return
list=[ QPointF(self.elementAt(l).x,self.elementAt(l).y) for l in range(self.elementCount())]
if p == self.elementCount()-1 : # last point
del list[p-2:]
elif p==0 :
del list[0 : 3]
else:
del list[p-1 : p+2]
self.remakepath(list)
def insertpoint(self,p):
if p <0 or p > self.elementCount() :
print('select point')
return
# vec = QPointF( (self.elementAt(p).x - self.elementAt(p-3).x )/4 ,
# (self.elementAt(p).y - self.elementAt(p-3).y )/4 )
# po = QPointF( self.elementAt(p-3).x , self.elementAt(p-3).y )
# list1=[ po+vec , po+vec+vec , po+ vec+vec+vec ]
p1=(self.elementAt(p-3).x , self.elementAt(p-3).y )
p2=(self.elementAt(p-2).x , self.elementAt(p-2).y )
p3=(self.elementAt(p-1).x , self.elementAt(p-1).y )
p4=(self.elementAt(p).x , self.elementAt(p).y )
tt=1/4
list1=[]
for l in range(1,4): # Bezier calculate
t=l*tt
x = (1-t)**3*p1[0] + 3*(1-t)**2*t*p2[0] + 3*(1-t)*t**2*p3[0] + t**3*p4[0]
y = (1-t)**3*p1[1] + 3*(1-t)**2*t*p2[1] + 3*(1-t)*t**2*p3[1] + t**3*p4[1]
list1.append(QPointF( x , y))
list=[ QPointF(self.elementAt(l).x,self.elementAt(l).y) for l in range(self.elementCount())]
list[ p-1 : p-1 ] = list1
self.remakepath(list)
def remakepath(self,list):
path = QPainterPath( list[0] )
for i in range (1, len(list) ,3):
path.cubicTo( list[i] , list[i+1] , list[i+2] )
self.__init__(path)
self.makepointrect()
def contains_m(self, p ) : # QPointF p
for l in self.pointrect :
if l[1].contains(p) :
return l[0] # hit point
if self.contains(p):
return -1 # contain but point
else :
return -2 # not contain
class makepath(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('hogetta')
self.setGeometry( 500, 50 , 500 , 500 )
self.setMouseTracking (True)
self.plist=[]
self.plist.append( QPointF(10,250) ) # start point
self.plist.append( QPointF(100,100) ) #
self.plist.append( QPointF(300,200) ) #
self.plist.append( QPointF(490,250) ) #
# self.path = QPainterPath( self.plist[0] )
self.path = mypath(QPointF(10,250) )
for i in range (1, len(self.plist) ,3):
self.path.cubicTo( self.plist[i] , self.plist[i+1] , self.plist[i+2] )
self.a = QPointF(4,4) # Tolerance in detecting point
self.pselected = -1 # selected point
self.pmoving = 0 # move index
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect( self.custommenu )
self.show()
def custommenu(self):
menu=QMenu(self)
action = QAction('Add Point', self)
action.triggered.connect(self.addpoint)
menu.addAction(action)
action = QAction('Insert Point', self)
action.triggered.connect(self.insertpoint)
menu.addAction(action)
action = QAction('Dell Point', self)
action.triggered.connect(self.delpoint)
menu.addAction(action)
menu.addSeparator()
action = QAction('Close Path', self)
action.triggered.connect(self.pathclose)
menu.addAction(action)
action = QAction('Open Path', self)
action.triggered.connect(self.pathopen)
menu.addAction(action)
menu.addSeparator()
action = QAction('Point Info', self)
action.triggered.connect(self.showelem)
menu.addAction(action)
action = QAction('Save Path', self)
action.triggered.connect(self.pathsave)
menu.addAction(action)
action = QAction('Load Path', self)
action.triggered.connect(self.pathload)
menu.addAction(action)
menu.exec_(QCursor.pos ())
def pathsave(self):
fname=os.path.join(os.path.dirname(__file__) , 'qtsave')
f = QFile(fname)
f.open( QIODevice.WriteOnly )
ds = QDataStream(f)
ds.__lshift__(self.path)
f.close()
def pathload(self):
self.path=mypath(QPointF(0,0))
fname=os.path.join(os.path.dirname(__file__) , 'qtsave')
f = QFile(fname)
f.open( QIODevice.ReadOnly)
ds = QDataStream(f)
ds.__rshift__(self.path)
f.close()
self.path.makepointrect()
self.update()
# self.showelem()
def pathclose(self):
self.path.setclosed(1)
self.path.movepoint( 0 ,QPointF( self.path.elementAt(0).x,self.path.elementAt(0).y ),0)
self.update()
def pathopen(self):
self.path.setclosed(0)
def showelem(self):
print('show element')
for i in range(self.path.elementCount() ):
print('format: {} x: {} y: {}'.format( self.path.elementAt(i).type ,
self.path.elementAt(i).x , self.path.elementAt(i).y ) )
def addpoint(self): # ctrl + click
e=self.mapFromGlobal( QCursor.pos() )
p=QPointF( self.path.elementAt(self.path.elementCount()-1).x,
self.path.elementAt(self.path.elementCount()-1).y )
q = QPointF( (e.x() - p.x()) /3 , (e.y() - p.y())/3 )
self.path.cubicTo(p+q,p+q+q,p+q+q+q)
self.path.makepointrect()
self.update()
def delpoint(self):
self.path.delpoint(self.pselected)
self.update()
def insertpoint(self):
self.path.insertpoint(self.pselected)
self.update()
def mouseMoveEvent(self ,e):
if self.pmoving == -1 : # not Dragging
l = self.path.contains_m( e.pos() ) # <------------判定メソッド
if self.pselected >= 0 : # point selecting
if l >= self.pselected-1 and l <= self.pselected+1 :
return # same point -- ignore
elif l< 0 :
return
else :
type1=self.path.elementAt(l).type
type2=self.path.elementAt(l-1).type
if type1==0 or type1==1 or (type1==3 and type2==3):
self.pselected = l # reselect point
self.setWindowTitle('selected point {}'.format(l))
else:
if self.pselected == l : # case of -1 -2 , same--ignore
return
else :
self.pselected = l
else : # dragging
self.path.movepoint( self.pmoving ,e.pos(), e.modifiers().__eq__(Qt.ShiftModifier))
self.update()
def mousePressEvent(self ,e):
if e. modifiers().__eq__(Qt.ControlModifier): # ctrl
if self.pselected==-2:
self.addpoint()
return
elif self.pselected==-1:
self.addpoint()
return
else:
self.insertpoint()
return
l = self.path.contains_m( e.pos() ) # <------------判定メソッド
if l >= 0 :
self.pmoving = l
self.setWindowTitle('moving point {}'.format(l))
def mouseReleaseEvent(self ,e):
self.pmoving = -1
if self.path.contains_m( e.pos() ) == -2 :
self.pselected = -2 # release selected mode
self.update()
def paintEvent (self,e):
painter= QPainter()
painter.begin(self)
if self.pselected==-2:
painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) )
painter.drawPath(self.path)
elif self.pselected >=-1:
painter.setPen( QPen( Qt.blue , 2, style=Qt.SolidLine ) )
painter.drawPath(self.path)
for i,l in enumerate(self.path.pointrect) :
type1=self.path.elementAt(i).type
type2=self.path.elementAt(i-1).type
if type1==0 or type1==1 or (type1==3 and type2==3):
painter.drawRect(l[1])
if self.pselected >=0:
painter.setPen( QPen( Qt.red , 2, style=Qt.SolidLine ) )
p = QPointF( self.path.elementAt(self.pselected).x , self.path.elementAt(self.pselected).y)
p2 = QPointF( self.path.elementAt(1).x , self.path.elementAt(1).y) \
if self.pselected==self.path.elementCount()-1 else \
QPointF( self.path.elementAt(self.pselected+1).x , self.path.elementAt(self.pselected+1).y)
p3 = QPointF( self.path.elementAt(self.path.elementCount()-2).x ,
self.path.elementAt(self.path.elementCount()-2).y) if self.pselected==0 else \
QPointF( self.path.elementAt(self.pselected-1).x , self.path.elementAt(self.pselected-1).y)
painter.drawRect( QRectF( p-self.a , p+self.a))
if self.pselected!=self.path.elementCount()-1 :
painter.drawLine( p , p2 )
painter.drawRect( QRectF( p2-self.a , p2+self.a))
if self.pselected!=0 or self.path.closed :
painter.drawLine( p , p3 )
painter.drawRect( QRectF( p3-self.a , p3+self.a))
painter.end()
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = makepath()
sys.exit(app.exec_())
-
考え方
派生クラスの内部で実装
①内部でポイントをリストに書き出す
②リストベースで追加削除の操作をする
③リストを元に操作パスを作成して自分自身にセットする -
閉じているか開いているかで動作は違うか??
-
削除するポイントpを選択させてから実行
①始点 p , p+1 , p+2
②終点 p , p-1 , p-2
③中点 p , p+1 , p-1
これで場合分けする -
挿入するポイントpを選択して実行(前に挿入)
・p-1 の位置に三点作って挿入する
・pとp-3をベジェカーブで四分割した時の三点を挿入する
・始点の選択は許さない(終点に追加すればいいから)
コメント(0)