PDA

Voir la version complète : Petits Scripts, Python



ksaa
14/05/2011, 14h18
Hello
voir du code sur c4d m’a rendu un peu nostalgique... ça m’a rappelé la bonne époque où je m’éclater avec du basic sur mon vieux pc :)
Je partagerai donc, ici, les rares scripts que j’écrirai…. vu que je ne suis pas du tout un programmeur

Script I : Basic Test Scene (http://safina3d.blogspot.com/2011/05/c4d-script-basic-test-scene.html)
Script II: pour mes tests de rendu j’utilise souvent la forme ci-dessous, donc j'ai voulu automatisé un peu sa création...et surtout apprendre à créer une petite interface graphique


http://img848.imageshack.us/img848/5373/92608141.png


import c4d
from c4d import gui, documents

class automatik(gui.GeDialog):
btnID = 111
def CreateLayout(self):

self.GroupBegin(999,c4d.BFH_CENTER,2,0,"paramètre :");
self.GroupBorder(c4d.BORDER_GROUP_IN)
self.GroupBorderSpace(10,5,10,5)
self.SetTitle("automatik")

self.AddStaticText(0, c4d.BFV_CENTER, 0, 0, "Hauteur ", c4d.BORDER_NONE)
self.AddEditText(3,c4d.BFH_SCALEFIT|c4d.BFV_SCALEF IT,200,10)

self.AddStaticText(1,c4d.BFV_CENTER, 0, 0, "Profondeur", c4d.BORDER_NONE)
self.AddEditText(4, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT,200,10)

self.AddStaticText(2, c4d.BFV_CENTER, 0, 0, "Largeur", c4d.BORDER_NONE)
self.AddEditText(5, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT,200,10)
self.GroupEnd()
self.AddButton(self.btnID,c4d.BFH_SCALEFIT|c4d.BFV _SCALEFIT, 100, 15, "Valider")

return True

def Command(self, id, msg):
if id == self.btnID:
hauteur = 400+(100*(float(self.GetString(3))))
profondeur = 100*(float(self.GetString(4)))
largeur = 100*(float(self.GetString(5)))
Construction(hauteur, profondeur, largeur)
self.Close()
return True

class Construction():
def __init__(self, htr, pfr, lgr):
self.htr = htr
self.pfr = pfr
self.lgr = lgr

#-------------------------- Mise en place :
scene = documents.GetActiveDocument()

# Creer spline
sp = c4d.SplineObject(4,4)
# Positions des points
sp.SetPoint(0,c4d.Vector(0,0,pfr*-1))
sp.SetPoint(1,c4d.Vector(0,0,600))
sp.SetPoint(2,c4d.Vector(0,400,1000))
sp.SetPoint(3,c4d.Vector(0,htr,1000))
sp.Message(c4d.MSG_UPDATE)

#Tangentes des pts
sp.SetTangent(1,c4d.Vector(0,0,0),c4d.Vector(0,0,2 00))
sp.SetTangent(2,c4d.Vector(0,-200,0),c4d.Vector(0,0,0))

#Extrude
objExt = c4d.BaseObject(c4d.Oextrude)
objExt.SetPhong(True,False,80)
objExt.SetName("Extrude")
objExt[c4d.EXTRUDEOBJECT_MOVE] = c4d.Vector(lgr,0,0)

sp.SetAbsPos(c4d.Vector((-1*lgr/2),0,pfr-1000/2))

scene.InsertObject(objExt)
scene.InsertObject(sp,objExt)


print("Done!")
#-----------------------------------------

if __name__=='__main__':
dlg = automatik()
dlg.Open(0,c4d.DLG_TYPE_MODAL_RESIZEABLE,700,300, 200)
c4d.EventAdd()

oli_d
14/05/2011, 15h08
Super !
bon apparemment t'es pas un débutant et te rassure je ne suis pas programmeur non plus (enfin j'sais pas si ça te rassure !)

Quelques petites remarques :


Pourquoi utiliser du texte éditable (.AddEditText()) alors que tu as besoin plutôt de nombre donc utilise directement AddEditNumber ou encore mieux avec le flèches AddEditNumberArrows , cela t'évitera en plus après de convertir les string en float.
attention avec documents.GetActiveDocument(), surtout ailleurs que dans les scripts, utilise directement simplement doc qui est prêt à l'emploi. Tu peux avoir des problèmes dans les tags ou générateurs lors du rendu car vu qu'il fait une copie du document ce n'est pas forcément celui qui restera actif(je sais pas si je suis clair là !)
pour ton exemple un générateur d'objet python(menu python) avec des données utilisateurs serait beaucoup plus adapté car tu pourrais ensuite modifier ton objet à la volée comme un objet paramétrique. Ensuite sinon tu peux carrément faire un véritable objet paramétrique sous forme de plugin.

oli_d
14/05/2011, 15h26
Allez hop le corrigé de l'exercice générateur d'objet ( à n'ouvrir que quand tu auras réussi seul !)

Après tu peux améliorer, en créant un réglage pour le rayon de courbure du plan

2937

ksaa
14/05/2011, 15h50
lol... ok je vais essayer de le faire ;)
Au fait comment on fait pour avoir accès à des variables qui sont dans d'autre fonctions ou class ?
pour ce qui est de (.AddEditText()), j'avais commencé par utilisé AddEditNumber, mais si je me souviens bien je ne pouvais entrer que des valeurs comprises entre 0 et 100.
merci oli_d

ksaa
14/05/2011, 19h28
le P. generator c'est une autre histoire :) c'est exactement ce que je voulais faire au début, car dans mon premier code je ne pouvais plus modifier l'objet une fois créé.
là, j'ai viré l'interface graphique et je l'ai remplacé par l'UserData ( la moitié du code est déjà partie)... et j'ai même rajouter un scale pour faire varier la taille de l'objet... j'espère que le code est bon cette fois ;)
Maintenant, une question un peu bête... comment faire pour l'utiliser lol? Avec le script manager je pouvais enregistrer le script, le placer dans un bouton dans l’interface de c4d, mais avec le générateur.... ?
Je pense que c’est le moment de voir la correction hein :)

un petit aperçu :

http://www.youtube.com/watch?v=7H8N4iEMM0Q

code:

import c4d
from c4d import gui, documents

sp = c4d.SplineObject(4,4)
objExt = c4d.BaseObject(c4d.Oextrude)
doc.InsertObject(objExt)
doc.InsertObject(sp,objExt)

def main():
echelle = op[c4d.ID_USERDATA,6]
hauteur = 400+(100*op[c4d.ID_USERDATA,2])
profondeur = 100*op[c4d.ID_USERDATA,3]
largeur = 100*op[c4d.ID_USERDATA, 4]

#-------------------------- Mise en place :

# Positions des points
sp.SetPoint(0,c4d.Vector(0,0,profondeur*-1))
sp.SetPoint(1,c4d.Vector(0,0,600))
sp.SetPoint(2,c4d.Vector(0,400,1000))
sp.SetPoint(3,c4d.Vector(0,hauteur,1000))
sp.Message(c4d.MSG_UPDATE)

#Tangentes des pts
sp.SetTangent(1,c4d.Vector(0,0,0),c4d.Vector(0,0,2 00))
sp.SetTangent(2,c4d.Vector(0,-200,0),c4d.Vector(0,0,0))

#Extrude

objExt.SetPhong(True,False,80)
objExt.SetName("Extrude")
objExt[c4d.EXTRUDEOBJECT_MOVE] = c4d.Vector(largeur,0,0)

sp.SetAbsPos(c4d.Vector((-1*largeur/2),0,profondeur-1000/2))

objExt.SetAbsScale(c4d.Vector(echelle))
c4d.EventAdd()
print ("done!")

oli_d
15/05/2011, 06h41
Super, je sens en toi un futur redoutable plugineur !

Dans le générateur la fonction main doit retourner un objet c4d qui peut évidemment avoir des enfants. Donc dans ton cas renvoie ton objExt dans lequel tu aura mis ta spline avec sp.InsertUnder(objExt) (tout est dans le corrigé).

Hors des scripts évite comme la mort les c4d.EventAdd, car tu risques de déclencher des boucles infinies, tu provoques un évènement qui relance le code qui provoque un évènement qui relance le code qui....

L'inconvénient des générateurs c'est que l'on ne peut pas les garder dans l'interface comme un plugin ou un script, il faut les copier/coller ou alors les mettre sous forme de fichier dans la médiathèque.

Donc pour faire ce que tu veux il faut faire un plugin et là ça se complique un peu. Je ne sais pas si tu as les exemples de plugins de Maxon, je me permets de les mettre en annexe au cas où (mettre les sous-dossier dans le dossier plugin de l'appli et redémarrer c4d). Regarde du côté du plug Py-RoundedTube et dans la doc dans le module c4d.plugins, surtout du côté de ObjectData

2941

oli_d
15/05/2011, 10h57
en fait je t'ai dit que l'on ne pouvait pas stocker un générateur, mais on peut faire un script qui génère un générateur !
Dans la première partie on génère les UserData.
Ensuite le code n'est pas forcément très lisible, car on envoie le code du générateur dans le champ relatif.

C'était pour la beauté du geste, mais le plugin reste quand même plus logique ...


import c4d

def AddRealData(obj,nom="UD"):
if obj is None: return
bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_REAL) #create default container
bc[c4d.DESC_NAME] = nom #rename the entry
bc[c4d.DESC_UNIT]=c4d.DESC_UNIT_METER # en metre
bc[c4d.DESC_MIN]=0 #limite minimum
element = obj.AddUserData(bc) #add userdata container
obj[element] = 300 #assign a value

def main():
doc.StartUndo()
obj = c4d.BaseObject(1023866)#objet python
AddRealData(obj,nom="hauteur")
AddRealData(obj,nom="profondeur")
AddRealData(obj,nom="largeur")

obj[c4d.OPYTHON_CODE]="""import c4d

def spline_ksaa(haut, prof, larg):
sp = c4d.SplineObject(4,c4d.SPLINETYPE_BEZIER)
sp.SetPoint(0,c4d.Vector(0,0,prof*-1))
sp.SetPoint(1,c4d.Vector(0,0,600))
sp.SetPoint(2,c4d.Vector(0,400,1000))
sp.SetPoint(3,c4d.Vector(0,haut,1000))
sp.SetTangent(1,c4d.Vector(0,0,0),c4d.Vector(0,0,2 00))
sp.SetTangent(2,c4d.Vector(0,-200,0),c4d.Vector(0,0,0))
sp.Message(c4d.MSG_UPDATE)

objExt = c4d.BaseObject(c4d.Oextrude)
objExt.SetPhong(True,False,80)
objExt.SetName("Extrude")
objExt[c4d.EXTRUDEOBJECT_MOVE] = c4d.Vector(larg,0,0)

sp.SetAbsPos(c4d.Vector((-1*larg/2),0,prof-1000/2))

sp.InsertUnder(objExt)
return objExt

def main():
haut = op[c4d.ID_USERDATA,1]
prof = op[c4d.ID_USERDATA,2]
larg = op[c4d.ID_USERDATA,3]
return spline_ksaa(haut, prof, larg)"""


obj.SetName("GeneKsaa")
doc.InsertObject(obj)
doc.AddUndo(c4d.UNDOTYPE_NEW, obj)
doc.SetActiveObject(obj)
doc.EndUndo()
c4d.EventAdd()

if __name__=='__main__':
main()

ksaa
15/05/2011, 15h30
Super!
je vais essayer de décortiquer un peu le code ;)
Merci pour ton investissement personnel dans ce que je fais

j'ai regardé aussi un peu pour faire un plugin et il y a quelques trucs qui m’échappent genre :

# be sure to use a unique ID obtained from www.plugincafe.com
PLUGIN_ID = 1025250 quésako ?

L’exemple Py-RoundedTube est effectivement très intéressant, d'abord le dossier du plugin :
- un fichier Py-RoundedTube.pyp : c'est juste un fichier .py renommé en. pyp ou le résultat d'une manip?
- un dossier "res" :
---> un tif ( ok)
---> un fichier c4d_symbols.h (?)
---> un fichier .str (semble contenir des noms de variables et le texte affiché => remplace l'UD ?)
---> un autre fichier .h (?) (l’id de chaque composantes ??)
---> et un fichier .res (contient des informations : valeur min, max, etc...)
Je pars du principe que python est un langage interprété et non compilé, donc à quel moment ces fichiers ont été généré ?

Merci.

Jean-Laurent
15/05/2011, 16h06
Allez, je vais tenter une réponse mais on risque de me corriger par la suite, voir de me taper sur les doigts. :icon_mrgreen:

Les différents plugin de C4D doivent tous avoir une ID unique.
Si deux plugin ont la même ID le risque c'est que des variables possédant le même nom soient utilisées malencontreusement dans le mauvais plugin.
Donc à la base, l'attribution d'un numéro ID unique pour chaque plugin permet d'éviter cette interaction.
Dans le temps l'attribution d'un numéro se faisait via le site plugincafe.com mais je ne sais pas si c'est toujours d'actualité.
Tu peux toujours rentrer un numéro au hasard pour tes tests il y a peu de chance que tu ais un problème.

Pour le reste je ne connais que les plugin coffee donc je ne peux pas vraiment t'aider.
En coffee on pouvait tout faire tenir dans un seul fichier .cof mais il était vivement conseillé pour la modularité de séparer ça dans plusieurs fichiers contenu dans le dossier res (ressources).
En particulier un fichier .str aussi il me semble, pour tout ce qui est texte. Et qui permettait par exemple de pouvoir facilement changer la langue du plugin. Sans rechercher au milieu du code toutes les parties textes.
De même pour les variables les plus importantes.
Les fichiers .cof n'étaient pas compilés non plus, donc on créait soit même ces fichiers.

ksaa
16/05/2011, 17h57
Merci Jean-Laurent
J’ai suivi ton conseil >> un nombre au hasard, et hop plus de message d’erreur :)

J’ai pu enfin, faire de ce petit bout de code un plugin détectable par c4d ! j’ai passé la quasi-totalité de mon temps à lire la doc, mais j’avoue que la plupart du temps elle me laisse sur ma faim… beaucoup de questions et peu de réponses, et les exemples sont très rares,… peut être que je l’exploite mal… maybe…
Voilà ou j’en suis pour le moment, le code génère bien un objet, Mais sans possibilité de modification :( et c’est là où je bloque…


import c4d
import os
from c4d import plugins

# be sure to use a unique ID obtained from www.plugincafe.com
PLUGIN_ID = 1020684681

class Lobj(plugins.CommandData):

def Execute(self, doc):
sp = c4d.SplineObject(4,4)
objExt = c4d.BaseObject(c4d.Oextrude)

#--------------------------- UserData -----

doc.InsertObject(objExt)
doc.SetActiveObject(objExt)
noms = ["Hauteur","Profondeur","Largeur","Arrondi","Echelle"]
valeurs = [5,3,15,100,1]
for comp, val in zip(noms, valeurs):
bc = c4d.GetCustomDatatypeDefault(c4d.DTYPE_REAL) #create default container
bc[c4d.DESC_NAME] = comp #rename the entry
UD = objExt.AddUserData(bc) #add userdata and set to container
objExt[UD] = val


hauteur = 400+(100*objExt[c4d.ID_USERDATA,1])
profondeur = 100*objExt[c4d.ID_USERDATA,2]
largeur = 100*objExt[c4d.ID_USERDATA, 3]
arrondi = objExt[c4d.ID_USERDATA,4]
echelle = objExt[c4d.ID_USERDATA,5]

#-------------------------- Mise en place :

# Positions des points
sp.SetPoint(0,c4d.Vector(0,0,profondeur*-1))
sp.SetPoint(1,c4d.Vector(0,0,(1000-arrondi*2)))
sp.SetPoint(2,c4d.Vector(0,arrondi*2,1000))
sp.SetPoint(3,c4d.Vector(0,hauteur,1000))
sp.Message(c4d.MSG_UPDATE)

#Tangentes des pts
sp.SetTangent(1,c4d.Vector(0),c4d.Vector(0,0,arron di))
sp.SetTangent(2,c4d.Vector(0,-arrondi,0),c4d.Vector(0))
#Arrondi

#Extrude
objExt.SetPhong(True,False,80)
objExt.SetName("Extrude")
objExt[c4d.EXTRUDEOBJECT_MOVE] = c4d.Vector(largeur,0,0)

sp.SetAbsPos(c4d.Vector((-1*largeur/2),0,profondeur-1000/2))

objExt.SetAbsScale(c4d.Vector(echelle))
sp.InsertUnder(objExt)


c4d.EventAdd()
print ("done!")
return True


if __name__ == "__main__":
icon = c4d.bitmaps.BaseBitmap()
icon.InitWith(os.path.join(os.path.dirname(__file_ _), "res", "obj.tif"))
c4d.plugins.RegisterCommandPlugin(PLUGIN_ID, "L-Object", 0, icon, "Generateur de L - Object", Lobj())

valkaari
17/05/2011, 00h52
"Plugin IDs 1000001-1000010 are reserved for development. You may use these freely to test scripts, but must request an ID in order to release them."

Donc les ID de tests existent déjà. Il ne faut absolument pas prendre un chiffre au hasard. En cas de conflit, c'est l'utilisateur qui sera incapable de savoir pourquoi il y a un soucis.

Prendre un ID ça se fait sur le site de maxon, http://www.plugincafe.com/forum/developer.asp
Il faut s'enregistrer, donner un nom (ou pas) et cliquer. C'est immédiat et gratuit.

Pour l'aide ou les exemples, il y a http://www.plugincafe.com/forum/default.asp

Y a vraiment pas tout (et de loin) mais on trouve toujours un bout de code pour se prendre la tête dessus avant de trouver.


edit après zieutage du code :

Pour ce qui est de ton code, il n'est exécuté qu'une seule fois. C'est pour ça que la modification des DU ne change rien à ton extrusion. (si c'est ce que tu veux faire)

Si tu veux faire ce genre de chose, il faut passer par la création d'un objet générateur ou par un tag. Ces objets sont présents dans l'interface de c4d et sont "recalculé" à chaque frame ou suivant les messages de mises à jours de l'interface (touche A, mouvement de la souris, clic, modif d'un autre plugin etc etc)

Tu pourrais rajouter un tag python qui prendrait les DU pour mettre à jour le reste. (spline, extrusion). (qui devrait être le plus facile mais le plus bancale)

Si tu veux plonger un peu plus dans le développement, essayes de reconstruire l'objet extrusion directement. (faudra construire les poly, les UVs, gérer le niveau de subdivision)

Si tu veux juste faire un peu plus propre, tu peux créer un générateur d'objet, mettre ton extrusion et ta spline dedans caché et gérer la modifications via des DU.

Jean-Laurent
17/05/2011, 09h47
"Plugin IDs 1000001-1000010 are reserved for development. You may use these freely to test scripts, but must request an ID in order to release them."

Donc les ID de tests existent déjà. Il ne faut absolument pas prendre un chiffre au hasard. En cas de conflit, c'est l'utilisateur qui sera incapable de savoir pourquoi il y a un soucis.


Je savais bien qu'on allait me taper sur les doigts. :icon_mrgreen:
Je me souviens bien des ID test mais le problème c'est qu'avec 10 ID on en a vite fait le tour, surtout lorsqu'on est en phase d'apprentissage et qu'on test tout un tas de choses. Les plugin exemples utilisent ces mêmes ID d'ailleurs si ma mémoire est bonne ce qui en interdit déjà certaines.
Par contre pour le plugin définitif c'est autre chose. C'est bien pourquoi je parle de "tests".
A moins d'avoir 1000 plugin installés sur sa machine les chances d'avoir un plugin en conflit durant la phase de test en rentrant une ID de l'ordre du million sont quand même faibles.
L'erreur de programmation est hélas bien plus fréquente. (surtout pour moi :sweatdrop:)

ksaa
18/05/2011, 18h34
merci valkaari !!

Et voilà ! J’ai enfin ressui à créer un plugin ... le tout premier ... Ce fut long...surtout pour un si petit code, mais au moins j’ai pu apprendre quelques nouveaux trucs :)



http://www.youtube.com/watch?v=k0o555dZd_w

ha... la mod me manque maintenant !

je mets le plug aussi, ça peut être utile pour les débutants comme moi

valkaari
18/05/2011, 18h44
cool, tu peux aussi ajouter un bend dedans pour courber en plus le plan. enfin un neutre, le bend, l'extrude avec la spline dedans.

(j'ai pas regardé le code)

oli_d
19/05/2011, 17h06
Un grand bravo !

J'ai regardé le code (en vitesse) et j'ai rien à redire -> bonne maîtrise du python SDK (software developpment kit)
c'est mieux par les temps qui courent qu'une bonne, qui ne maîtrise pas le python de DSK (bon ok je ->)

ksaa
22/05/2011, 00h41
Bonsoir, bonsoir…
Lol… pas mal le jeu de mots

Cette fois j’ai essayé de faire un script qui imite la fonction "isolation" qui est dans 3ds max, je la trouve très pratique, surtout dans des scènes surchargées ...
Bon le code est encore à améliorer vu que j’ai des difficultés avec la class selection

Utilisation :
- sélection d'un objet puis >> isoler
- modif de l'objet... ou pas
- désélectionner tout puis >> isoler


Code Bonus


import foot
print (" !!! LE LOSC CHAMPION !!! Banzai !! ")

ksaa
23/05/2011, 02h32
re Bonsoir...

Quelques petites corrections dans le code de "Isoler" >> le dernier {que j'avais posté}, fonctionnait de façon partielle… dès que j'ouvrais un fichier .c4d dont l’emplacement est diffèrent que celui en cours >> IOError



http://www.youtube.com/watch?v=S8X5sidIVpU

oli_d
23/05/2011, 09h25
Bon ben rien à redire, je sens que je vais bientôt te demander des conseils ....

ksaa
23/05/2011, 11h17
Je suis encore loin… Je galère avec tout ce qui est sélection .... par exemple, je n’arrive pas à trouver une fonction pour sélectionner tous mes objets… ou encore, savoir le nombre d’objets dans la scène :( ...je peux surement passer par une boucle... mais est ce que y'a plus simple ?

oli_d
23/05/2011, 12h34
pour sélectionner il faut utiliser BaseList2D.SetBit(c4d.BIT_ACTIVE) ou DelBit pour déselectionner ou ToggleBit pour inverser.

Les fonctions recursives sont pas faciles à comprendre mais pour ce genre de chose c'est redoutable, juste un exemple qui sélectionne tous les objets et qui les compte (variantes d'utilisation de cette fonction récursive ici)
(http://frenchcinema4d.fr/showthread.php?72481-Python-Scripts-phidek)
import c4d


def select_objs(obj,i=0):
"""fonction recursive pour sélectionner tous les objets et renvoie le nombre d'objets"""
while obj :
obj.SetBit(c4d.BIT_ACTIVE) #sélection de l'objet pour déséléctionner remplacer SetBit par DelBit
i+=1 #compteur d'objets
i+=select_objs(obj.GetDown())# recursion : la fonction s'appelle elle-meme
obj = obj.GetNext()
return i

if __name__=='__main__':
obj = doc.GetFirstObject()
if obj :
print select_objs(obj) #imprime le nombre d'objet dans la console et les sélectionne tous
c4d.EventAdd()

ksaa
23/05/2011, 13h06
merci oli ;)

xs_yann
23/05/2011, 21h19
Je partage un petit bout de code suite au post de oli :

import c4d

def doOnHierarchy(start, func):
i = 0
while start:
func(start)
i += 1 + doOnHierarchy(start.GetDown(), func)
start = start.GetNext()
return i

def selectObject(op):
op.SetBit(c4d.BIT_ACTIVE)

def addPhongTag(op):
op.MakeTag(c4d.Tphong);

def changeName(op):
op.SetName(op.GetName() + " new");

if __name__=='__main__':
print doOnHierarchy(doc.GetFirstObject(), selectObject)
doOnHierarchy(doc.GetFirstObject(), addPhongTag)
doOnHierarchy(doc.GetFirstObject(), changeName) # J'ai appelé la fonction plusieures fois pour l'exemple
c4d.EventAdd()


En gros c'est une factorisation du code qui permet d'appliquer une fonction sur chaque objet de la hiérarchie.
Ca ne fonctionne peut-être pas pour tous les cas d'utilisations et ça peut surement être amélioré mais ça évite de surcharger et de répéter le code avec des boucles. ;)

oli_d
24/05/2011, 06h18
C'est effectivement beaucoup plus propre et pratique, merci xs_yann !

ksaa
24/05/2011, 11h35
Génial!
je ne savais pas qu'on pouvait passer une fonction en argument... et encore moins l'appeler dans son propre corps !!
en fait je n'ai jamais essayé ... ça me donne des idées :)
Merci à vous deux... je pense avoir saisi le principe

xs_yann
24/05/2011, 22h50
je ne savais pas qu'on pouvait passer une fonction en argument... et encore moins l'appeler dans son propre corps !!
en fait je n'ai jamais essayé ... ça me donne des idées :)

C'est le principe des pointeurs sur fonctions en C/C++, en Python apparement les fonctions sont considérées comme des objets. D'après ce que j'ai vu il y a même moyen de simuler une variable statique de cette manière :


def func():
print func.x
func.x += 1
func.x = 0

def main():
func()
func()
func()

ksaa
20/06/2011, 11h49
Merci Yann
Bientôt j’aurai un peu plus de temps libre pour revoir les bases de python.

Aujourd’hui aussi j'ai fait un petit plugin, je n’ai pas résisté …:)
En mod, Un mesh avec 0 triangle fait toujours plaisir. Ce petit plug permet de trouver tous les triangles et de les grouper dans une sélection, pour faciliter la correction.


>>> zTriangle <<< (http://www.4shared.com/file/-c9aBpoT/zTriangle.html)


http://www.youtube.com/watch?v=Da5kuerGDyc

Sir Gong
20/06/2011, 21h41
Merci Ksaa, je viens de tester ça fonctionne bien, avec quelques petites bizarreries :

(au cas où ça aurait une incidence, je suis en R12 Studio sur Os X)
Lorsque je sélectionne le module, j'ai dans un premier temps la boîte de dialogue de la commande "optimiser" qui apparaît :
3110

une fois validé, la fenêtre de zTriangle apparaît :
3111

petite suggestion : mettre "zTriangle" au lieu de "CINEMA 4D Studio" dans l'intitulé.

Et pour finir, si je fais un commande+z (ou ctrl+z) pour annuler, au lieu de revenir à l'objet avant application du module, j'ai droit à toutes les étapes intermédiaires réalisées par le plug. (c'est un peu embêtant)

Je ne sais pas si tu peux ou as envie d'améliorer, c'est déjà bien utile en l'état, merci ;)

ksaa
23/06/2011, 08h46
Merci Sir Gong :)

J’ai refait mon code en partie pour le rendre un peu plus " propre " :
- plus de boite " optimiser "
- Undo possible
- boites de dialogue plus adaptées
- Suppression des n-gons (résultat = quad + triangle)


zTriangle 1.1 (http://www.4shared.com/file/YLlxNSma/zTriangle_v11.html?)

Sir Gong
23/06/2011, 10h03
Beaucoup mieux :thumbup1: merci !

ksaa
03/03/2012, 00h04
Bonsoir mes amis!

Quelqu’un pourrait me faire un petit tuto pour une intro ? hum ... désolé… fallait que ça sorte

Plus sérieusement, question beaucoup plus simple, enfin, je l'espère,
en python, quelle est la signification du ~ (tilde), j’ai fait quelques recherches mais …
voici un petit exemple :



a = 2
b = ~a
print b


la console affiche -3 comme résultat, et la o_O! que quoi cmment ?
Merci d'avance

valkaari
03/03/2012, 01h20
l'opération Bitwise Inversion

ou invert(a)

Tous les bits à 0 passent à 1 et inversement. Pratique pour avoir l'inverse d'une matrice pour passer de coordonnées local à globale et ... inversement.

C'est un des opérateurs de python.
http://docs.python.org/library/operator.html

César Vonc
03/03/2012, 01h51
Mais alors, ~5 devrait donner 2, vu que 5 est 101 et 2 est 010, non ?

Or ~5 donne -4.

valkaari
03/03/2012, 02h42
sur 8 bits 5 -> 00000101
L'inverse donne donc 11111010

Le bit de poids le plus fort (celui à gauche) détermine le signe (sur un entier signé). Du coup on sait qu'on a un chiffre négatif et ça change tout.

00000000 -> 0
11111111 -> -1
11111110 -> -2
11111101 -> -3
11111100 -> -4
11111011 -> -5
11111010 -> -6

si tu fais un print ~5 il te donne -6.

en fait tu prends les 0 pour avoir le nombre en base 10 et t'enlève 1

par contre 11111111 si on stocke une valeur non signé celà donne 255 et pas -1

ksaa
03/03/2012, 06h55
hmm je vois ... faut quand même une petite révision en math :) Merci valkaari!

j'ai essayé avec des matrices,




import c4d

def main():
obj = doc.GetActiveObject()
a = obj.GetMg()
print a.off #Vector(200,200,200)
b = ~a
print b.off #Vector(-200,-200,-200)

if __name__=='__main__':
main()



Donc, si je ne dis pas de bêtises,

local coord * (M) = global coord.
global coord * ~(M) = local coord.

César Vonc
03/03/2012, 11h02
Ah d'accord, je pige mieux. Merci Val !

valkaari
04/03/2012, 03h10
J'avoue que je fais toujours un test rapidos pour vérifier s'il faut multiplier par la matrice ou par son inverse. ( y a des trucs con comme ça qui n'entrent pas dans mon crâne de piaf)

Mais oui c'est bien ça.

Je précise que c'est bien une coordonné que l'on multiplie par une matrice.

VecteurA * MatriceB = VecteurC

Ne pas confondre avec le produit scalaire (dot product) qui sert à avoir l'angle entre deux vecteurs ou le produit vectoriel (cross product) qui sert à avoir le vecteur perpendiculaire.
(et je dois aussi vérifier à chaque fois qui fait quoi aussi pour ces trucs là, ça rentre pas, qu'est ce que vous voulez :icon_banghead:)

Mais je sais qu'il faut pas les confondre ^^

ksaa
04/03/2012, 19h08
Miam miam… que du bon!
Justement en parlant de matrices, y a-t-il un lien entre la matrice de la vue c.a.d :


bd = doc.GetActiveBaseDraw()
M = bd.GetMg()

et la position d’un point par exemple ?

valkaari
04/03/2012, 21h32
Aucune, les positions dans c4d sont définie en fonction de l'origine du monde ou du parent (global ou local).
Elles sont stocké sous forme de matrice composé de 4 vecteurs. Un pour la position et 3 pour l'orientation/échelle.

Chaque vue possède une camera et une méthode de projection (perspective/orthogonal etc) que l'on ajoute un objet camera ou non.
Comme chaque objet dans un espace 3D, la camera possède des coordonées. C'est ce que tu récupères avec le GetMg d'un baseDraw.

Je n'ai jamais utilisé les fonctions pour jouer avec les informations projeté sur la vue, mais la class BaseDraw est hérité de BaseView qui elle possède des fonctions pour ce genre de choses à priori.

BaseView.WS(p)
World to screen conversion. Converts p from world space to screen space (pixels relative to the view), and returns the conversion. The orthogonal distance to the world point is stored in world units in the Z axis of the result.


Mais encore une fois, je n'ai jamais utilisé ces fonctions.

oli_d
05/03/2012, 21h13
Val a tout juste, il y a un bon exemple dans la doc dans le Py-LiquidPainter qui passe de la vue à la scène avec bd.SW(point).


(et je dois aussi vérifier à chaque fois qui fait quoi aussi pour ces trucs là, ça rentre pas, qu'est ce que vous voulez :icon_banghead:)


ça me rassure c'est la même chose pour moi ...

César Vonc
15/03/2012, 13h59
Au sujet de la classe BaseSelect (http://code.vonc.fr/c4d/python/modules/c4d/BaseSelect/index.html#BaseSelect.SelectAll) et sa fonction SelectAll(min, max), ses deux arguments semblent inversés.

D'après la doc, min est le premier élément à sélectionner, et max le dernier.

Or pour que la cela fonctionne, il faut intervertir min et max :


import c4d

def main():
if not op: return
bs = op.GetPolygonS()
nbp = op.GetPolygonCount()

bs.SelectAll(nbp-1, 2) # Sélectionne tous les polygones à partir du second.
#bs.SelectAll(2, nbp-1) # Ne sélectionne rien.

c4d.EventAdd()

if __name__=='__main__':
main()Pourrait-on me confirmer cela ?

ksaa
15/03/2012, 15h30
yep! pareil chez moi
malheureusement la doc de python et incomplète et contient quelques erreurs :(

César Vonc
30/06/2012, 20h50
J'ai un petit soucis concernant un calcul d'angle à partir de vecteurs, voici le problème :

http://img338.imageshack.us/img338/7159/image3juz.png

J'ai un vecteur v absolu (donc défini sur les axes x, y, z du monte).
J'ai une matrice Mr = ((0;0;0), Xr, Yr, Zr).
Je cherche à calculer l'angle A entre Yr et v', v' étant une sorte de projection du vecteur v sur le plan XrZr.

J'ai passé la matinée dessus, en jouant avec MulV, GetAngle et compagnie... mais sans succès.
J'aimerais éviter les sinus et cosinus car le vecteur v peut-être orienté dans n'importe quel sens.

Je pense qu'il faudrait trouver les nouvelles coordonnées du vecteur v par rapport à Mr, mais je vois pas comment.


C'est sûrement simple, mais j'ai la tête comme dans une machine à laver.

valkaari
30/06/2012, 21h03
pas spécialement tout compris (quand on est pas dedans c'est parfois un peu flou).

Par contre pour avoir un angle entre deux vecteurs, il suffit de faire un dot product. v1.dot(v2)

Par contre, je ne sais plus s'il donne l'angle en radian directement ou s'il donne le cos de l'angle. Auquel cas il faut faire un inverse cos.

edit :
évidement si le résultat est 0 c'est donc que les deux vecteurs sont parallèle (attention aux approximation de python ou de ciname4D 0.0001 != 0))

Il me semble aussi que tu peux déterminer si l'angle est aigu ou obtu si le résultat est négatif ou positif.

César Vonc
30/06/2012, 23h13
Ah ben, tu m'as donné une idée, pas avec le produit scalaire mais avec le produit vectoriel !

Si je calcule le produit vectoriel entre v et Zr, je pourrai calculer son angle par rapport à Yr, qui vaudrait A + 90°

Donc :
A = c4d.utils.VectorAngle(v.Cross(Mr.v3), Mr.v2) - math.pi / 2

Et ça marche ! ^^

valkaari
01/07/2012, 00h57
Et le dot product entre v et Yr te donne pas le cos de l'angle ?

César Vonc
01/07/2012, 12h45
Si, mais l'angle entre Yr et v est différent de celui entre Yr et v'.

Jean-Laurent
01/07/2012, 14h53
Heu ... :huh:

Je n'ai peut-être pas tout compris mais c'est très curieux que ça marche.

Petit rappel d'ordre général.
Le produit scalaire ne donne pas un angle. Pour faire simple, c'est le "produit" de deux vecteurs qui donne un scalaire. Bref, un nombre.
Mais ce nombre (scalaire) dépend effectivement de l'angle entre les vecteurs.
Si A est la norme du premier vecteur (sa longueur) et B la norme du second. x l'angle entre les deux:
Le produit scalaire vaudra: A.B cos x.
Si les vecteurs sont unitaires (A=1 et B=1) on a bien effectivement la valeur du cosinus de l'angle. (Compris entre -1 et 1)
Le cosinus n'est ni en radian ni en degré. C'est uniquement avec le cos-1 qu'on devra se soucier de l'unité de l'angle.

Mais le plus intéressant c'est la signification "physique" possible de ce produit scalaire. C'est comme si on multipliait la norme du premier vecteur par la projection du deuxième vecteur sur ce même premier vecteur. Ou inversement puisque le produit est symétrique.

vecteur A scalaire vecteur B = A * projection de B sur A = B * projection de A sur B.

Du coup on comprend facilement et intuitivement que si le produit scalaire est nul c'est que la projection d'un vecteur sur l'autre est nul, et qu'ils sont à 90°.
Si l'angle est aigu il est positif, s'il est obtus négatif.


Pour le produit vectoriel. C'est le produit de deux vecteur qui donne un troisième vecteur perpendiculaire aux deux autres et dont la norme est équivalente à la surface du parallélogramme formé par les deux premiers vecteurs.

La norme du produit vectoriel vaut donc: A.B sin x avec les notations précédentes.
Et de la même manière qu'un produit scalaire permet de remonter au cosinus, un produit vectoriel permettra d'obtenir le sinus. Ainsi que la direction perpendiculaire aux deux vecteurs de départ.

Là encore, de manière intuitive on voit que le produit vectoriel est maximal si les deux vecteurs sont à 90° l'un de l'autre. (Aire d'un rectangle) et vaut 0 si ils sont colinéaires.


Là où je suis très sceptique c'est quand tu fais le produit vectoriel de v et Zr. Le résultat n'a aucune raison de se trouver dans le plan XrYr.
Ce ne sera le cas que si v est lui-même dans le plan XrYr.
Du coup je ne comprends pas bien ton calcul de A+90. :huh:

Tu peux facilement avoir les projections de v sur Yr et Xr avec le produit scalaire.
(v scalaire Yr)/(norme Yr) par exemple donne celle sur Yr. Si Yr est unitaire c'est encore plus simple.
Puis à partir des projections l'angle A se déduit facilement.
(projection sur Xr/projection sur Yr) = tan (A)

Il y a sans doute plus simple aussi. Peut-être ton A+90 il faut que je regarde plus en détail, mais j'ai un gros doute.

César Vonc
01/07/2012, 15h24
Merci pour ces précisions, Jean-Laurent.


Là où je suis très sceptique c'est quand tu fais le produit vectoriel de v et Zr. Le résultat n'a aucune raison de se trouver dans le plan XrYr.En fait j'en ai justement déduis que si, dans tous les cas. Si on imagine une plaque en équilibre sur l'axe Zr, sa normale sera toujours dans le plan XrYr, donc si cette plaque s'appuie également sur le vecteur v, sa normale sera le produit vectoriel de v et Zr.

Et vu qu'elle est perpendiculaire, en calculant son angle avec Yr je tombe sur v' en virant 90°.

Jean-Laurent
01/07/2012, 16h02
Tu as parfaitement raison.
Tous les vecteurs perpendiculaires à Zr (ce qui est le cas du produit vectoriel) sont contenus dans Xr, Yr.
C'est logique maintenant que tu le dis.
C'est moi qui suis à côté de la plaque. :icon_redface:

César Vonc
29/09/2012, 10h29
J'ai un petit soucis pour créer un tableau à trois dimensions.

Avec la première méthode, je fais :
tab1 = [[[False]*(zmax)]*(ymax)]*(xmax)

Avec la seconde méthode, je fais trois boucles initialisant la case à chaque fois.



import c4d

def main():
xmax = 2
zmax = 2
ymax = 2

tab1 = [[[False]*(zmax)]*(ymax)]*(xmax)

tab2 = []
for x in xrange(xmax) :
tab2.append([])
for y in xrange(ymax) :
tab2[x].append([])
for z in xrange(zmax) :
tab2[x][y].append(False)

print tab1 == tab2 # Vrai
tab2[1][1][1] = True
tab1[1][1][1] = True
print tab1 == tab2 # Faux
print tab1
print tab2

if __name__=='__main__':
main()


Jusqu' là tout va bien, le résultat est le même, mes deux tableaux sont identiques lorsque je les compare :

tab1 = [[[False, False], [False, False]], [[False, False], [False, False]]]
tab2 = [[[False, False], [False, False]], [[False, False], [False, False]]]

Mais dès que je veux modifier une valeur, plus rien ne va !

tab1[1][1][1] = True
tab2[1][1][1] = True

tab1 = [[[False, True], [False, True]], [[False, True], [False, True]]]
tab2 = [[[False, False], [False, False]], [[False, False], [False, True]]]


tab1 ne me modifie pas la bonne case. Comment cela se fait ?

oli_d
29/09/2012, 11h49
à mon avis c'est une histoire de label quand tu multiplies le tableau

problème identique que quand tu fais lista = listb cela crée une sorte de pointeur (en vrai un label, j'ai jamais vraiment essayé de comprendre la différence)

http://forum.hardware.fr/hfr/Programmation/Python/copie-liste-sujet_119386_1.htm (http://forum.hardware.fr/hfr/Programmation/Python/copie-liste-sujet_119386_1.htm)

xs_yann
29/09/2012, 14h12
Salut,

oli_d a raison, la multiplication de la liste créée une référence, pour obtenir une copie il faudra soit faire appel au constructeur "list()" soit avec "[:]"

Quelques pistes :


tab1 = [[x[:] for x in [[False]*xmax]*ymax] for x in xrange(zmax)]
------
tab1 = [[[False for i in xrange(xmax)] for j in xrange(ymax)] for k in xrange(zmax)]
------
tab1 = {}
tab1[(1, 2, 3)] = True

print tab1[(1, 2, 3)]

try:
print tab1[(1, 1, 1)]
except KeyError:
pass