PDA

Voir la version complète : Espacement régulier d'une sélection de points



Sir Gong
05/02/2015, 12h21
Bonjour,
Est-ce qu'un programmeur saurait pondre un petit script ou autre pour espacer régulièrement une sélection de points selon un axe ?
Par exemple sur l'image ci-dessous, que les points sélectionnés soient espacés régulièrement horizontalement, sans changer de position verticalement. (ou avec l'option de le faire aussi verticalement avec une case à cocher?)

14061

Aucune idée de la difficulté de la tâche, mais si jamais c'était pas trop galère à mettre en œuvre, ce serait bien pratique.

César Vonc
05/02/2015, 20h37
Salut Sir Gong.

Essaie de coller ça dans le gestionnaire de script, ça devrait te dépanner si t'en as besoin rapidement :

Tu peux changer l'espacement et l'axe au début.


import c4d
from c4d import Vector

def main():
#
espace = 10.0
axe = "-z"
# Axe à changer par : x, -x, y, -y, z ou -z
#

if not op : return
if not op.CheckType(c4d.Opoint) : return

inv = False
if (axe[0] == "-") :
inv = True
axe = axe[1:]

axeVec = Vector(0,1,1)
posAxe = Vector(espace,0,0)
if axe == "y" :
axeVec = Vector(1,0,1)
posAxe = Vector(0,espace,0)
elif axe == 'z' :
axeVec = Vector(1,1,0)
posAxe = Vector(0,0,espace)

doc.StartUndo()
doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)

pts = []
bs = op.GetPointS()

for i, sel in enumerate(bs.GetAll(op.GetPointCount())) :
if not sel : continue
posv = op.GetPoint(i)
pos = posv.x
if axe == "y" : pos = posv.y
elif axe == 'z' : pos = posv.z

pts.append((pos, i))

pts.sort()
if inv : pts.reverse()
posCum = op.GetPoint(pts[0][1]).__rxor__(posAxe.GetNormalized())
if inv : posAxe *= -1

for pt in pts :
i = pt[1]
pos = op.GetPoint(i)
pos = pos.__rxor__(axeVec) + posCum
op.SetPoint(i, pos)
posCum += posAxe

op.Message(c4d.MSG_UPDATE)

doc.EndUndo()

c4d.EventAdd()


if __name__=='__main__':
main()

Sir Gong
05/02/2015, 22h11
Merci beaucoup César,
Si je comprends bien (rien n'est moins sûr), je dois renseigner la valeur de l'espace et l'axe (lignes 6 et 7) avant de l'éxécuter.
Est-ce qu'il est possible, au lieu de donner une valeur d'espacement, que le script trouve lui même la valeur à répartir entre le 1er et le dernier point sélectionnés ?

Enfin, là ça me dépanne déjà bien, j'obtiens un espacement régulier, et il me suffit ensuite d'entrer une valeur de la taille pour redimensionner l'ensemble de la sélection. Merci encore.
:kiss:

Math1712
06/02/2015, 07h30
Bonjour Sir Gong si j'ai bien compris la valeur = la distance entre le premier et le dernier point sur l'axe / par le nombre de points c'est ça ?

leBigYO
06/02/2015, 08h38
mmh, en gros c'est ce qu'on retrouve avec l'outil "alignement" d'illustrator mais appliqué à des points. ça peut s'avérer très utile ça

oli_d
06/02/2015, 08h43
Je me suis permis une variante qui prend la vue active comme référence et répartit sur l'axe horizontal de la vue entre le point le plus à gauche et le plus à droite.


import c4d



def main():
#on vérifie que l'on est bien en mode point
if not doc.GetMode() ==c4d.Mpoints : return
#on vérifie si il y a bien un objet sélectionné ...
if not op : return
#et si cet objet est de type point (spline ou objet polygonal)
if not op.CheckType(c4d.Opoint) : return

#on récupère le BaseSelect des points
bs = op.GetPointS()
mg = op.GetMg()
#on récupère la fenêtre de dessin active
bd = doc.GetActiveBaseDraw()

dico_ptsvue = {}
pts_vueX = []
for i,sel in enumerate(bs.GetAll(op.GetPointCount())) :
if not sel: continue
pt_vue = bd.WS(op.GetPoint(i)*mg)
dico_ptsvue[i] = pt_vue
pts_vueX.append((pt_vue.x,i))

pts_vueX.sort()
id_pt_depart = pts_vueX[1]
dist = (pts_vueX[-1][0]-pts_vueX[0][0])/(len(pts_vueX)-1)
posX = pts_vueX[0][0]
inv_mg = ~mg

doc.StartUndo()
doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)

for x,i in pts_vueX:
pt_vue = dico_ptsvue[i]
pt_vue.x = posX
pt = bd.SW(pt_vue)*inv_mg
op.SetPoint(i,pt)
posX+=dist


op.Message(c4d.MSG_UPDATE)
doc.EndUndo()
c4d.EventAdd()

if __name__=='__main__':
main()

Sir Gong
06/02/2015, 10h06
@ Math1712 : oui, c'est ça.

@ leBigYO : voilà, c'est exactement ça.
Utile, mais ce n'est pas le genre de truc dont on a besoin tous les jours. Généralement les outils de C4D (pinceau en mode lissage, ponçage) suffisent, mais pour le cas présent le lissage bouge les points adjacents, ce qui ne va pas.

@ oli_d : merci, ça marche impec, maintenant il faudrait une case à cocher ou je ne sais quoi pour choisir l'axe et ne pas avoir à tourner la géométrie et ce serait parfait.
:thumbup1:

César Vonc
06/02/2015, 22h11
Pff en plus il fait ça en 15 lignes, Oli_d. ^^

Bien joué !

oli_d
07/02/2015, 06h36
Merci César, mais il y en a un peu plus que 15 ...

@Sir Gong : plutôt qu'une case à cocher je te mets le code ci-dessous pour la répartition verticale selon la vue. Si tu te fais ensuite deux boutons, un avec le vertical, l'autre l'horizontal, est-ce que cela suffirait à te combler de bonheur ?


import c4d



def main():
if not doc.GetMode() ==c4d.Mpoints : return
if not op : return
if not op.CheckType(c4d.Opoint) : return

bs = op.GetPointS()
mg = op.GetMg()
bd = doc.GetActiveBaseDraw()

dico_ptsvue = {}
pts_vueY = []
for i,sel in enumerate(bs.GetAll(op.GetPointCount())) :
if not sel: continue
pt_vue = bd.WS(op.GetPoint(i)*mg)
dico_ptsvue[i] = pt_vue
pts_vueY.append((pt_vue.y,i))

pts_vueY.sort()
id_pt_depart = pts_vueY[1]
dist = (pts_vueY[-1][0]-pts_vueY[0][0])/(len(pts_vueY)-1)
posY = pts_vueY[0][0]
inv_mg = ~mg

doc.StartUndo()
doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)

for x,i in pts_vueY:
pt_vue = dico_ptsvue[i]
pt_vue.y = posY
pt = bd.SW(pt_vue)*inv_mg
op.SetPoint(i,pt)
posY+=dist

op.Message(c4d.MSG_UPDATE)
doc.EndUndo()
c4d.EventAdd()

if __name__=='__main__':
main()

Sir Gong
07/02/2015, 12h16
C'est parfait, oli-d merci beaucoup !
(et à César aussi pour la 1ere mouture)
:thumbup1: