PDA

Voir la version complète : SubdiviCerce & Fractales



César Vonc
06/04/2010, 19h03
Dernière mise à jour : 12 mail 2010.

SubdiviCerce

Script permettant de subdiviser une cerce selon la méthode Catmull-Clark ou certaines courbes fractales.

Le motif fractal peut être facilement personnalisable.


http://img405.imageshack.us/img405/6918/subdivisericone.png Télécharger (http://cesarvonc.fr/subdivicerce)

http://img208.imageshack.us/img208/1834/subdivicerce.png

http://img187.imageshack.us/img187/527/fractalesdc.png

Plus d'images :
http://www.frenchcinema4d.fr/forum/index.php?topic=26154.msg491333#msg491333
http://www.frenchcinema4d.fr/forum/index.php?topic=26154.msg491501#msg491501
http://www.frenchcinema4d.fr/forum/index.php?topic=26154.msg492454#msg492454

Dernière édition : Personnaliser le motif fractal.

-------


Bonjour,

J'aimerais copier un objet à partir du Lien de mes Données Utilisateur, mais tel que je l'ai fait, ça ne fonctionne pas :


main(doc,op)
{
var objet_source = op#ID_USERDATA:3;
var copie_objet = objet_source->GetClone(0);
{
doc->InsertObject(copie_objet,op,NULL);
}
}

Je débute en Coffee, j'ai essayé en plaçant un FindObject un peu n'importe où autour de mon #ID_USERDATA:3, mais je dois mal m'y prendre.

http://img688.imageshack.us/img688/9172/image3ox.png

J'aimerais ensuite que cette copie se retrouve dans #ID_USERDATA:4 (j'imagine que c'est la même chose que de récupérer l'info, mais en sens inverse).

Il se trouve que cet objet est un cerce que j'aimerais subdiviser automatiquement, pour éviter d'avoir à le faire à la main avec l'outil subdivision (je n'ai rien trouvé à ce sujet pour le faire).

Ceci pour un traitement extérieur dans XPresso qui est déjà plus ou moins prêt, c'est vraiment ce morceau dans Coffee que je n'arrive pas à faire malgré mon acharnement (rien que de trouver comment intégrer une Donnée Utilisateur dans Coffee, j'ai mis un temps fou).

Votre aide serait la bienvenue !

César Vonc
06/04/2010, 19h19
Ah non, je n'ai rien dit ! Ça fonctionne mais ça me le copiait en sous-objet de l'objet contenant la propriété Coffee.

Du coup j'ai ajouté :

main(doc,op)
{
var objet_source = op#ID_USERDATA:3;
var copie_objet = objet_source->GetClone(0);

var activation = op#ID_USERDATA:1;

if ((activation==1)
{
doc->InsertObject(copie_objet,doc,NULL);
op#ID_USERDATA:4 = copie_objet;
}
}

Ça fonctionne, mais il me créé une copie indéfiniment... quelle condition ajouter pour n'en faire qu'une ?

En changeant op par doc, l'expression ne fonctionne plus, alors que je veux simplement qu'il me le copie dans le document.

Il reste toujours de soucis de subdivision où je nage complètement.

valkaari
06/04/2010, 20h11
à chaque mise à jour de l'interface, le code est exécuté et remis à neuf donc une simple condition ne suffira pas.

moi je mettrais une case à cocher que je désactiverais une fois l'objet crée. la condition pour créer l'objet que la case à cochée soit vrai donc cochée.

pour insérer ton objet t'as juste à mettre NULL comme objet parent
doc->InsertObject(copie_objet, NULL,NULL) ;

ou
doc->InsertLast (copie_objet); pour le mettre en bas de ta hiérarchie. (enfin un truc du genre ><)

César Vonc
06/04/2010, 22h31
Ah merci, ça marche bien en mettant NULL.

Pour la case à cocher, c'est mon :
var activation = op#ID_USERDATA:1;

Le problème, c'est que le logiciel s'actualise déjà deux fois le temps que j'aille décocher la case ; je me retrouve avec deux copies.

Sinon, des idées pour subdiviser ?

valkaari
06/04/2010, 22h36
non ben décoche la case dans ton code ^^

op#ID_USERDATA:1 = false

avant l'insertion dans le doc.

pour la subdivision tu peux faire des callcommand(numéro de la commande)

pour t'aider tu peux ouvrir le gestionnaire de commandes, taper "script log" et cliquer sur le bouton exécuter. ça t'ouvrira la fenêtre des commandes passées. tu coups tu peux faire tes commandes à la souri et copier coller les commandes du script log.

César Vonc
06/04/2010, 23h59
Pratique dis donc, ces commandes directement visibles dans l'Historique de Script (je ne trouvais rien avec script log, j'imagine que c'est la même chose).

J'ai donc trouvé ma commande : CallCommand(14047); // Subdiviser...
Que j'applique après être passé en mode point : CallCommand(12139); // Points

Je les incruste donc à la suite de l'insertion de mon objet et... ça ne marche pas.

J'ai pu trouver sur un site qu'il fallait sélectionner l'objet avant avec doc->SetActiveObject();

Je l'ajoute, mais rien à faire. Seule la sélection de l'objet fonctionne.

main(doc,op)
{
var objet_source = op#ID_USERDATA:3;
var copie_objet = objet_source->GetClone(0);
var activation = op#ID_USERDATA:9;


if (activation == true)
{
op#ID_USERDATA:9 = false;
doc->InsertObject(copie_objet,NULL,NULL);
doc->SetActiveObject(copie_objet);
CallCommand(12139); // Points
CallCommand(14047); // Subdiviser...
op#ID_USERDATA:4 = copie_objet;
}
}

Où ai-je faux ?

valkaari
07/04/2010, 01h48
heu en fait je crois bien qu'on ne peut pas passer de paramètres à un callcommand. Ce qui exclus l'utilisation de toutes les commandes ou il faut en entrer (c'est à dire beaucoup) dans un tag coffee.... j'avais oublié ...

je sais pas si on peut utiliser la commande SendModellingCommand dans un tag coffee.

oli_d
07/04/2010, 07h28
Je confirme CallCommand() et SendModellingCommand() ne fonctionnent à ma connaissance ni dans un tag ni dans un noeud COFFEE. Il faut passer par un plugin COFFEE.

Par contre cela fonctionne dans le gestionnaire de script. Il y a un exemple d'utilisation ici (en bas) : http://www.frenchcinema4d.fr/forum/index.php?topic=25880.0

Sinon tu as le Python et Py4D avec mon tuto :http://www.frenchcinema4d.fr/forum/index.php?topic=25634.0. Là tu peux envoyer des commandes d'où tu veux, et si tu débutes ce sera à mon avis plus simple que le COFFEE

Si tu nous en disais un peu plus sur la finalité du truc, ta démarche m'a l'air un peu complexe ...

César Vonc
07/04/2010, 11h13
Je cherche à lisser une cerce avec la méthode Catmull-Clark, car je trouve ça fort dommage d'avoir B-Spline, Akima, Cubique, Bézier, mais pas Catmul-Clark qui est utilisé dans l'HyperNurbs.

J'ai pu dénicher plus ou moins le procédé du lissage Catmull-Clark sur la toile.
Coffee me sert à copier la cerce automatiquement et la subdiviser, Xpresso à bouger les points pour créer l'arrondi.

Pour l'instant, ça fonctionne si je pense à subdiviser à la main. Une fois ceci fait, je n'ai plus qu'à dire à quels points de ma cerce je veux du lissage (Il suffit de faire rapidement défiler le champs Indice du point pour affecter toute la cerce).

J'ai aussi rajouté l'intensité de la courbure, (de grandes valeurs permettent de denteler la cerce).

http://img63.imageshack.us/img63/7379/lissagecatmullclark.png


Je vais essayer avec le gestionnaire de script, seulement, est-ce qu'il est facilement transportable (pour en faire profiter aux autres, par exemple) ?

oli_d
07/04/2010, 13h15
Je vais essayer avec le gestionnaire de script, seulement, est-ce qu'il est facilement transportable (pour en faire profiter aux autres, par exemple) ?


Oui c'est très facile soit tu fournis le code et les gens n'ont qu'a copier/coller dans un nouveau script ou alors tu peux dans le gestionnaire de script via le menu fichier/exporter un script. Il te fera un fichier .CSC que tu peux distribuer. L'utilisateur n'a ensuite qu'à placer ce fichier dans le dossier c4d/library/scripts et après redémarrage de c4d le script apparaîtra directement dans le menu modules externes/scripts personnalisés

Sinon pour ta subdivision regarde peut-être dans la doc du côté de SplineObject ->GetSplinePoint()pour calculer un certain nombre de point le long de ta spline et du côté de VariableChanged il y a un exemple de code pour ajouter des points à un point object (donc valable pour les splines)

BerTiN03
07/04/2010, 13h24
Vous êtes vraiment des malades... :o

César Vonc
07/04/2010, 18h06
Merci ! Ça marche beaucoup mieux avec le gestionnaire de script. J'ai finalement utilisé les CallCommand.

J'en ai donc profiter pour mettre tout mon Coffee en tant que Script personnalisé (avec une petite icône en plus, Ahlala ^^).
Est-ce qu'il y a moyen d'exécuter ce script via les Données utilisateur ? Ou bien de copier l'interface de mes Données utilisateur dans le Script personnalisé ?

Histoire d'avoir tous mes menus à un seul endroit (car pour l'instant, je règle avec mes Données utilisateur, exécute mon script séparément pour ensuite retourner dans mes Données utilisateur).

valkaari
07/04/2010, 18h31
Tu peux créer une interface complète avec un script. Le seul soucis c'est qu'une fois la fenêtre ouverte, elle prends la main tant qu'elle est ouverte ....

sauf si on peu créer une fenêtre nodal dans un script mais j'ai un doute...

César Vonc
07/04/2010, 22h11
Ah, et comment fait-on cela ?

J'ai trouvé quelques exemples d'interface sur le site de xs yann, mais elles sont en .cof, je n'arrive pas à les éditer pour voir ce qu'elles ont dans le ventre (j'ai essayé de les importer dans le gestionnaire de script, mais ça ne fonctionne pas).

valkaari
07/04/2010, 23h40
Via les fonctions CreateLayout de la classe GeDialog.

Il te faut les fonctions CreateLayout, Init, Command et Message au minimum pour fonctionner même si elles ne renvoient rien.
init est exécuté au lancement de l'interface, command c'est quand il se passe un truc dans ton interface, message c'est pour la gestion des messages comme un drag and drop.

Tient si tu veux un exemple y a le script de nitroman Xbreaker pour casser des trucs.
http://nitro4d.x10hosting.com/Download.html

Ca montre qu'il y a quand même pas mal de possibilités via le script manager.

Et pour info le SDK 9.5 coffee c'est le meilleur pour coffee et de loin.

César Vonc
09/04/2010, 16h12
J'ai tenté d'éplucher le script de nitroman pour voir comment était fait l'interface, mais vu la taille du script, j'ai vraiment du mal à m'y retrouver.

Je crois que tu surestimes mon niveau en Coffee, je ne le touche réellement que depuis une semaine.

J'ai tenté ça :


var t1="foireux";

/// Menu
class Menu:GeDialog {
public:
CreateLayout();
Command(id,msg);
Init();
}

Menu::Init()
{
}

Menu::CreateLayout()
{
SetTitle("ESSAI Interface");
AddStaticText(1,BFH_SCALEFIT,0,0,"Ceci est un essai "+ t1 +" d'interface",1);
}

Menu::Command(id,msg)
{
}

Pour afficher juste un texte, mais aucune fenêtre ne s'ouvre quand je lance mon script.

Je crois que le mieux (et le plus propre) serait que je convertisse tout mon Xpresso en script ; que ma cerce se subdivise et se lisse rien qu'en l'exécutant.

Pour l'instant, j'aimerais déjà arriver à afficher ma fenêtre d'interface.

valkaari
09/04/2010, 16h25
Donc la t'as construit ta class Menu qui est en fait la fenêtre.
Maintenant il faut créer un nouvel objet de cette class et appeler la commande open (de la class hérité GeDialog)


dlg=new(Menu);
dlg->Open(-1, -1);

Ces deux commandes tu peux les inclures dans une fonction main mais je suis pas certain que ce soit obligatoire dans un script

main (doc,op) {
dlg=new(Menu);
dlg->Open(-1,-1);
}

César Vonc
09/04/2010, 16h42
J'ai ajouté ce que tu m'as dit à la suite de mon script, mais aucune fenêtre ne s'ouvre pour autant (j'ai essayé avec et sans la fonction Main).

Que signifient les (-1,-1) de Open ?

valkaari
09/04/2010, 17h46
var t1="foireux";
var dlg;

class Menu:GeModalDialog {
public:
CreateLayout();
Command(id,msg);
Init();
};
Menu::Init()
{
}





Menu::CreateLayout()
{
SetTitle("ESSAI Interface");
AddStaticText(1,BFH_SCALEFIT,0,0,"Ceci est un essai "+ t1 +" d'interface",1);

return TRUE;
}





Menu::Command(id,msg)
{

}

main(doc,op) {
dlg =new(Menu);
dlg->Open(-1, -1);
}

testé

le main est visiblement obligatoire si tu veux faire des scipt complexe (avec création de class etc)

pour le -1,-1 il sagit des coordonnées de la position d'ouverture de la fenêtre, si c'est -1,-1 la fenêtre s'ouvre à la position courante de la souris.

César Vonc
09/04/2010, 21h45
Merci !
J'ai pu réaliser ma petite interface sans problème.

http://img709.imageshack.us/img709/2144/interfacelissagecerce.png

Le seul petit soucis est au niveau du bouton d'exécution. Actuellement il fonctionne (sans que je comprenne vraiment pourquoi), mais le fait de fermer la fenêtre active le script également.


J'aimerais ensuite sélectionner un point sur deux de ma cerce pour leur affecter à chacun une nouvelle position (position dépendante du point d'indice supérieur et inférieur de chaque point, et d'un petit calcul).

Alors je sais qu'il y a un GetSplinePoint() dans le lot, mais j'arrive vraiment pas à m'en servir...

Je vous montre mon code au cas où (j'ai tout commenté pour me faciliter la lecture) :



var t1="Lisser une cerce"; // Titre
var t2="Veuillez sélectionner une cerce à lisser."; // Message d'erreur
var t3="Infos"; // Infos
var t4="Lisser"; // Bouton
var t5="Intensité de la courbure"; // Pourcentage
var t6="Catmull-Clark : 25 %"; // Catmull-Clark.
var dlg;

//// MENU ////

class Menu:GeModalDialog {
public:
CreateLayout();
Command(id,msg);
Init();
};



/// INIT ///
Menu::Init()
{

// ID, Valeur initiale, Valeur Min, Valeur Max, Pas.
SetPercent(4,25,-9999,9999,1);

}



/// INTERFACE ///
Menu::CreateLayout()
{
// Titre.
SetTitle(t1);

// Texte statique.
AddStaticText(1,BFH_SCALEFIT,0,0,t3,1);

// Groupe
AddGroupBeginV(6,BFH_SCALEFIT,2,"Group",0);
{
AddGroupBorder(BORDER_GROUP_IN);
AddGroupBorderSpace(2,2,2,2); // Bordures d'espace (Gauche, Haut, Droite, Bas).
AddStaticText(5,0,0,0,t5 +" :",0); // Intensité de la courbure.
AddEditNumberArrows(4,BFH_SCALEFIT,50,10); // Pourcentage.
AddStaticText(0,0,0,0,t6,0); // Catmull-Clark.
AddGroupEnd();
}


// Bouton Lisser (ID, Style, Largeur, Hauteur, Texte).
AddButton(3, BFH_SCALEFIT,200,30,t4);

return TRUE;
}



/// COMMANDES ///
Menu::Command(id,msg)
{

if (id==3)
{
Close();
}

}


////////////////


main(doc,op)
{

// Je cherche l'objet sélectionné.
var source = doc->GetActiveObject();

// Si je ne le trouve pas : message d'erreur et quitter.
if (source==0){TextDialog(t2, DLG_OK);return(0);}

// Création et ouverture du Menu à la position de la souris.
dlg =new(Menu);
dlg->Open(-1, -1);

// Je créé une copie intégrale de l'objet sélectionné.
var copie = source->GetClone(0);

// J'insère l'objet copié dans le document.
doc->InsertObject(copie,NULL,NULL);

// Je le sélectionne.
doc->SetActiveObject(copie);

CallCommand(12236); // Autoriser les modifications
CallCommand(14047); // Subdiviser...

}

valkaari
10/04/2010, 01h53
il faut créer une fonction qui vas s'exécuter quand tu vas cliquer sur le bouton.

Dans ta fonction main tu n'auras que l'ouverture de la fenêtre.

Dans la classe "menu", la fonction "command" vas faire appel à la fonction dans la partie "if (id == 3)..."
Tu as crée le bouton avec cette ID.

pour ce qui est de la partie
if (source==0){TextDialog(t2, DLG_OK);return(0);}

tu devrais plutôt mettre

if (instanceof(source,Ospline){
TextDialof(t2,DLG_OK);
return FALSE;
}

par la même occasion, tu vérifies que l'objet sélectionné est bien une spline édité.

César Vonc
10/04/2010, 13h52
if (!instanceof(source,SplineObject))
{
TextDialog(t2,DLG_OK);
return FALSE;
}

Plutôt ?


J'ai fait ce que tu m'as dit, tout marche à merveille à condition de ne pas oublier de préciser la variable doc par :

var doc=GetActiveDocument();

dans la fonction Menu::Command(id,msg) (belle prise de tête pour trouver ça).


Une idée pour récupérer un point sur deux, de ma cerce ?

oli_d
10/04/2010, 17h45
Voilà un petit bout te code qui te permets de sélectionner un point sur 2 :



main(doc,op)
{
if (op->GetType()!= Ospline) return;//vérifier d'abord si c'est bien une spline
var bs = op->GetPointSelection(); //on récupère le BaseSelect
bs->DeselectAll(); //on déselectionne
var i;
for(i=0 ; i<op->GetPointCount() ; i+=2) //boucle avec un pas de 2
{
bs->Select(i);//on sélectionne le point
}
op->SetPointSelection( bs );//et on réaffecte le BaseSelect
}


Edit : le code sélectionne les points pairs, si tu veux les impairs initialise i à 1 : for(i=1;.......

César Vonc
11/04/2010, 19h45
Merci bien, Oli_D.

Si je comprends bien, la variable i indique chaque point sélectionné ?

Pour définir la position de chaque point, pourquoi ça ne fonctionne pas en ajoutant i->SetMg(position); ?

oli_d
11/04/2010, 20h48
la variable i est un chiffre entier, c'est ce qu'on appelle un iterateur.

Dans une boucle for() il y a trois paramètres

for(i=0 on initialise la valeur de base de la variable pour la première boucle,
for(i=0; i<op->GetPointCount() ce second paramètre est une condition, si elle est vrai la boucle continue, si fausse on sort de la boucle ( dans ce cas tant que la valeur de i est plus petite que le nombre de points on continue),
for(i=0; i<op->GetPointCount(); i+=2) là c'est la valeur que prend la varible i à chaque passage, donc dans ce cas elle va rajouter 2 à sa valeur on peut aussi le noter i=i+2, si tu veux que la valeur prenne 1 tu écriras i=i+1 ou la même chose en plus court : i++


si tu veux modifier des points :



main(doc,op)
{
var i;
for(i=0 ; i<op->GetPointCount() ; i+=2) //boucle avec un pas de 2
{
var pt = op->GetPoint(i); // on récupère le point n°i sous forme de vecteur
pt.y = 100; // on attribue 100 en Y attention c'est en local!
op->SetPoint(i,pt); //et on réinjecte ce vecteur dasn le point n°i
}
op->Message(MSG_UPDATE);//important pour que l'objet soit mis à jour
}

César Vonc
12/04/2010, 00h58
Très bien expliqué, tout est limpide.

J'ai donc pu (enfin) finir mon script de lissage de cerce qui marche désormais parfaitement ! :)

Quelques petites images :

À gauche : la cerce d'origine, à droite : celle après le script.
http://img217.imageshack.us/img217/9862/lc1.png

En changeant la courbure, on obtient un résultat amusant.
http://img532.imageshack.us/img532/956/lc2.png


Je vais donc pouvoir commencer à m'intéresser à de nouvelles méthodes de lissage que Catmull-Clark (telles que Doo-Sabin, Loop, Butterfly...), et passer à la 3d... dans une autre version de ce script.


Je peaufine encore l'icône et quelques petits trucs, puis je publierai le script (je renommerai mon sujet en conséquence (d'ailleurs, peut-être faudrait-il le déplacer dans le forum Coffee ?)).

Le seul petit bémol est d'avoir la fenêtre de la commande Subdiviser nous demandant de combien on veut subdiviser, alors qu'il faut mettre 2 (la valeur par défaut) pour que le script fonctionne correctement.
La cerce doit être de type Linéaire.

Voici le code actuel, quasi définitif :


var t1="Lisser une cerce"; // Titre
var t2="Veuillez sélectionner une cerce éditable."; // Message d'erreur
var t3="Lissage d'une cerce. Par César (cesar3d.fr)"; // Infos
var t4="Lisser"; // Bouton
var t5="Intensité de la courbure"; // Pourcentage
var t6="Catmull-Clark : 25 %"; // Catmull-Clark.
var t7="Je remercie Oli_D et Valkaari pour leur précieuse aide.";
var dlg;


//// MENU ////

class Menu:GeModalDialog {
public:
CreateLayout();
Command(id,msg);
Init();
};



/// INIT ///
Menu::Init()
{
SetPercent(4,25,-9999,9999,1); // ID, Valeur initiale, Valeur Min, Valeur Max, Pas.
}



/// INTERFACE ///
Menu::CreateLayout()
{
SetTitle(t1); // Titre.

AddStaticText(1,BFH_SCALEFIT,0,0,t3,1); // Texte statique.

AddGroupBeginV(6,BFH_SCALEFIT,2,"Groupe",0); // Groupe
{
AddGroupBorder(BORDER_GROUP_IN);
AddGroupBorderSpace(2,2,2,2); // Bordures d'espace (Gauche, Haut, Droite, Bas).
AddStaticText(5,0,0,0,t5 +" :",0); // Intensité de la courbure.
AddEditNumberArrows(4,BFH_SCALEFIT,50,10); // Pourcentage.
AddStaticText(0,0,0,0,t6,0); // Catmull-Clark.
AddGroupEnd();
}

AddButton(3, BFH_SCALEFIT,200,30,t4); // Bouton Lisser (ID, Style, Largeur, Hauteur, Texte).

AddStaticText(7,BFH_SCALEFIT,0,0,t7,1); // Texte de rerciements.

return TRUE;
}



/// COMMANDES ///
Menu::Command(id,msg)
{
if (id==3) // Si le bouton est enclenché...
{

var doc=GetActiveDocument(); // J'attribue la variable doc au document actif.
var source = doc->GetActiveObject(); // Je cherche l'objet sélectionné.
var copie = source->GetClone(0); // Je créé une copie intégrale de l'objet sélectionné.
doc->InsertObject(copie,NULL,NULL); // J'insère l'objet copié dans le document.
doc->SetActiveObject(copie); // Je sélectionne l'objet copié.
CallCommand(12236); // Autoriser les modifications
CallCommand(14047); // Subdiviser...

var bs = copie->GetPointSelection(); // Je récupère la sélection de base.
bs->DeselectAll(); // Je déselectionne.
var i;
for(i=0 ; i<copie->GetPointCount() ; i+=2) // Boucle avec un pas de 2. i=1 pour les points impairs.
{
var pt = copie->GetPoint(i); // Je récupère le point n°i sous forme de vecteur.
var pto = copie->GetPoint(i);
var ferme = (copie->GetContainer())->GetData(SPLINEOBJECT_CLOSED); // Je regarde si ma cerce est fermée.

var courbure = 0.02*GetPercent(4); // Je récupère la valeur de mon bouton d'ID n°4.

var sup = 1;
if ((i == copie->GetPointCount()-1) && (ferme==TRUE)) sup = -1; // Si i est le dernire point, le point suivant est le premier.
if ((i == copie->GetPointCount()-1) && (ferme==FALSE)) sup = 0;
var inf = -1;
if ((i == 0) && (ferme==TRUE)) inf = copie->GetPointCount()-1; // Si i est le premier point, le point précédent est le dernier.
if ((i == 0) && (ferme==FALSE)) inf = 0;

var ptsup = copie->GetPoint(i+sup); // Point supérieur.
var ptinf = copie->GetPoint(i+inf); // Point inférieur.

pt = (((ptsup*courbure) + (pto*(1-courbure)))*0.5) + (((ptinf*courbure) + (pto*(1-courbure)))*0.5); // J'attribue une nouvelle coordonnée à ce point.
copie->SetPoint(i,pt); // Et je réinjecte ce vecteur dans le point n°i.
bs->Select(i); // Je sélectionne le point.
}
copie->SetPointSelection( bs ); // Et je réaffecte la sélection de base.
copie->Message(MSG_UPDATE);// Important pour que l'objet soit mis à jour.
CallCommand(13324); // Tout désélectionner
Close();
}
}


////////////////

main(doc,op)
{

// Je cherche l'objet sélectionné et l'attribue à une variable.
var selection = doc->GetActiveObject();

// Si ce n'est pas une cerce : message d'erreur et quitter.
if (!instanceof(selection,SplineObject))
{
TextDialog(t2,DLG_OK);
return FALSE;
}

// Création et ouverture du Menu à la position de la souris.
dlg =new(Menu);
dlg->Open(-1, -1);

}

oli_d
12/04/2010, 07h46
:efface: Bravo et bienvenue dans le coté obscur de C4D !

valkaari
12/04/2010, 09h38
illllllll est des noooooooootreeeeeeeeeeeeeeeee il a fait son script comme les autrrrrrrrresssssss......

Par contre pour ce qui est de la subdivision tu peux la coder toi même, surtout si c'est pour mettre un point entre les points existant. C'est juste une moyenne des deux coordonnées si la spline est en linéaire.

Sir Gong
12/04/2010, 13h52
(d'ailleurs, peut-être faudrait-il le déplacer dans le forum Coffee ?)).
voilà c'est fait.

(c'est la seule phrase que j'ai comprise dans ce sujet)

César Vonc
17/04/2010, 13h21
Pour ce qui est de la subdivision, j'ai été voir l'exemple de l'utilisation de la fonction VariableChanged, mais c'est en C++ et non en Coffee.

J'ai pu ajouter un point grace à un exemple trouvé sur la toile, mais il s'ajoute à la fin de ma cerce, j'aimerais pouvoir lui dire à quel indice s'ajouter. Une idée ?


var cerce = doc->GetActiveObject();

var vc = new (VariableChanged); if(!vc) return;
var cnt = cerce->GetPointCount();

vc->Init(cnt,cnt+1); // Ajout d'un point.


var ok = cerce->MultiMessage(MSG_POINTS_CHANGED, vc);


var p1 = vector(10,10,10);

cerce->SetPoint(cnt,p1);

cerce->Message(MSG_UPDATE);

oli_d
18/04/2010, 07h12
Bon là prépare toi à un bon barbecue de neurones !

Le code ci-dessous permet de subdiviser une spline (j'ai pas mis les vérificateurs) avec la valeur que tu souhaites (un subdivision uniquement à 2 aurait été plus simple, mais moins drôle :mrgreen:)


var subd = 4;//nombre de subdivisions

var cerce = doc->GetActiveObject();
var vc = new (VariableChanged); if(!vc) return;
var cnt = cerce->GetPointCount();


var pts = new(array,cnt);//on crée un tableau pour stocker les anciennes valeur de point
var i;
for(i=0;i<cnt;i++)//boucle pour récupérer tous les points dans notre tableau
{
pts[i]=cerce->GetPoint(i);
}
var new_cnt = cnt + (cnt-1)*(subd-1); //pour chaque point sauf le dernier on rajoute nb de subdivision -1
vc->Init(cnt,new_cnt); // Ajout des points à la spline
var ok = cerce->MultiMessage(MSG_POINTS_CHANGED, vc);

var n=0; //on va utiliser plusieurs iterateurs
var m;
for(i=0;i<new_cnt;i+=subd) //boucle avec pas de la valeur de subdivison
{
cerce->SetPoint(i,pts[n]); //le point i correspond à notre ancien point n que l'on récupère de notre tableau
if(i<(new_cnt-subd))//pour éviter de lancer la boucle interne sur le dernier point
{
for(m=1;m<subd;m++)//boucle dans la boucle pour les points que l'on a créé
{
var vec = pts[n+1]-pts[n];//valeurs des deux anciens points au milieu desquels on a inséré
var dir= vnorm(vec); // direction du vecteur
var len = vlen(vec)/subd;//longeur du vecteur
var pos = len*dir*m + pts[n] ; //on rajoute au point [n] en fonction du point intermédiaire
cerce->SetPoint(i+m,pos);
}
}
n++; //on rajoute 1 à n
}

cerce->Message(MSG_UPDATE);

j'ai un peu commenté le code essaie de le décortiquer et de le comprendre ...puis pose des questions. Aide toi de dessins en numérotant les points pour bien piger

En gros :

on récupère la valeurs des anciens points dans un tableau
on crée nos points
on fait une boucle qui va récupérer la valeurs des anciens pour la remettre au bon endroit...
...puis pour les points intermédiaire on refait une boucle dans la boucle et on fait un savant calcul en utilisant des fonctions vectorielles (regarde un peu la doc sous vnorm() et vlen()


Il faudrait mettre tout ça sous-forme de fonction pour qu ce soit plus facilement réutilisable genre : subdivise(obj,nb_sub) ...

César Vonc
18/04/2010, 11h34
En effet, toutes ces boucles donnent le tournis.

Je crois avoir globalement pigé, sauf pour la manière dont tu calcules les coordonnées des points ajoutés ; j'aurais imaginé faire une moyenne entre les coordonnées du point supérieur et celui inférieur, donc les deux coordonnées additionnées × 0,5, mais là je vois :




var vec = pts[n+1]-pts[n];//valeurs des deux anciens points au milieu desquels on a inséré
var dir= vnorm(vec); // direction du vecteur
var len = vlen(vec)/subd;//longeur du vecteur
var pos = len*dir*m + pts[n] ; //on rajoute au point [n] en fonction du point intermédiaire



Une soustraction suivie de hiéroglyphes. :mrgreen:


Ton script marche à merveille, sauf qu'il ne prend pas en compte la dernière arête à subdiviser pour les cerces fermées.

J'ai donc voulu remédier ça en ajoutant :


var fermeture = (cerce->GetContainer())->GetData(SPLINEOBJECT_CLOSED); // Je regarde si ma cerce est fermée.
var ferme;
if (fermeture == FALSE) ferme = 1;
else ferme = 0;

[...]

var new_cnt = cnt + (cnt-ferme)*(subd-1);

Le dernier point est bien ajouté quand la cerce est fermée, sauf qu'il na pas de coordonnées attribuées (normal, vu que c'est une moyenne entre le point d'avant et le premier et non le point d'avant et celui d'après), mais vu que je comprends pas bien comment elles sont calculées, je suis bloqué.

oli_d
18/04/2010, 16h11
Pour les hiéroglyphes c'est du calcul vectoriel, j'me la pête grave avec ça mais ça fait pas longtemps que j'ai réellement compris !

C'est comme tout, une fois que tu connais c'est super simple
Je n'ai pas le temps de tout t'expliquer mais tu peux déjà regarder ça http://www.siteduzero.com/tutoriel-3-8890-les-vecteurs.html#ss_part_1

en gros:

la soustraction des deux vecteurs nous donne la valeur du segment
vlen() nous donne sa longueur
vnorm() nous donne son vecteur normalisé,c'est à dire sa direction
je divise la longueur par mon nombre de subdivisions, ce qui me donne la distance entre chaque point
et je multiplie par sa direction


L'histoire de la moyenne entre les deux vecteurs fonctionne dans le cas de subdivision par deux, mais doit être facilement adaptable aux autres cas de figure, c'est un autre chemin possible : teste ! Mais un conseil, essaie de comprendre ces calculs vectoriels qui ne sont au final pas trop compliqués parce que cela facilite beaucoup la vie en programmation.

Pour la spline fermée tu es bien parti. Un spline fermée a le même nombre de points qu'une ouverte, mais le dernier point va rejoindre le premier. Donc dans notre tableau lorsque l'on arrive sur le dernier point il faut que l'on reprenne le point 0 pour l'autre vecteur.

Voici le code qui fonctionne pour une spline fermée, je te laisse le soin de mettre des if pour qu'il fonctionne dans les deux cas:


var subd = 4;//nombre de subdivisions

var cerce = doc->GetActiveObject();
var vc = new (VariableChanged); if(!vc) return;
var cnt = cerce->GetPointCount();


var pts = new(array,cnt);//on crée un tableau pour stocker les anciennes valeur de point
var i;
for(i=0;i<cnt;i++)//boucle pour récupérer tous les points dans notre tableau
{
pts[i]=cerce->GetPoint(i);
}
var new_cnt = cnt + (cnt)*(subd-1); //pour chaque point sauf le dernier on rajoute nb de subdivision -1
vc->Init(cnt,new_cnt); // Ajout des points à la spline
var ok = cerce->MultiMessage(MSG_POINTS_CHANGED, vc);

var n=0; //on va utiliser plusieurs iterateurs
var m;
for(i=0;i<new_cnt;i+=subd) //boucle avec pas de la valeur de subdivison
{
cerce->SetPoint(i,pts[n]); //le point i correspond à notre ancien point n que l'on récupère de notre tableau

for(m=1;m<subd;m++)//boucle dans la boucle pour les points que l'on a créé
{
var x;
if(n==cnt-1) x=0; //si on arrive sur le dernier point de l'ancienne spline il faut prendre le premier...
else x=n+1; //pour la soustraction, sinon on prend n+1
var vec = pts[x]-pts[n];//valeurs des deux anciens points au milieu desquels on a inséré
var dir= vnorm(vec); // direction du vecteur
var len = vlen(vec)/subd;//longeur du vecteur
var pos = len*dir*m + pts[n] ; //on rajoute au point [n] en fonction du point intermédiaire
cerce->SetPoint(i+m,pos);
}

n++; //on rajoute 1 à n
}

cerce->Message(MSG_UPDATE);

Jean-Laurent
18/04/2010, 21h05
Très sympa ce sujet je l'avais raté.
Bravo les gars. :poucehaut:

J'en profite juste puisque vous êtes dans les vecteurs pour un légère digression sur la différence entre "direction" et "sens".
Car ces termes sont souvent très mal employés et source de confusion par la suite si on pousse plus loin.
C'est le cas sur le site du zero.
Un vecteur donnant les deux informations à la fois.

Verticale, horizontale, nord-sud ou est-ouest, c'est des directions.
Vers le haut, vers le bas, vers la gauche ou vers la droite un sens.

Si une personne fait Paris-Marseille par l'autoroute et l'autre Marseille-Paris elles vont dans la même direction. :o
Mais dans des sens opposés.

Les vecteurs (1,1) et (-1,-1) ont même direction, même norme mais des sens différents.

Bon courage à toi César pour la suite. :efface:

César Vonc
19/04/2010, 22h12
Je vois, c'est déjà moins obscur.

En manipulant les coordonnées des points, on peut calculer la position des points ajoutés en fonction de la subdivision etdes coordonnées du point d'avant et d'après (P1 et P2) :
(P1 × numéro du point + P2 × (subdivision - numéro du point)) / subdivision.

Pour une subdivision de 2, on obtient comme coordonnée du nouveau point :
(P1 × 1 + P2 × 1) / 2 (Une bête moyenne).

Pour une subdivision de 4 qui ajoute donc trois points :
Coordonnées du premier point ajouté : (P1 × 1 + P2 × 3) / 4
Le second : (P1 × 2 + P2 × 2) / 4
Le troisième : (P1 × 3 + P2 × 1) / 4

Ce qui revient d'ailleurs exactement au même que de calculer le barycentre entre deux points avec un facteur différent pour chaque point (j'ai d'ailleurs utilisé cette méthode pour calculer les nouvelles coordonnées des points originaux de la cerce (ceux qu'il fallait sélectionner une fois sur deux) pour mon lissage Catmull-Clark).
http://img705.imageshack.us/img705/7999/tutomodelhn2.png
Courbe originale (4 points), courbe subdivisée par la méthode Catmull-Clark (7 points), tracés de construction.

Ceci dit, c'est identique à la méthode avec les vecteurs (qui est sûrement plus économique puisqu'on ne multiplie qu'une seule fois), après tout, une coordonnée n'est qu'un vecteur.


Je prépare d'ailleurs un gros tutoriel sur la modélisation sous l'influence d'Hyper NURBS.


Bref, je crois que grace à Oli_D, mon script est bel et bien terminé, le voici. :)
J'ai ajouté une grande boucle pour pouvoir subdiviser plusieurs fois de suite automatiquement (et donc intensifier le lissage).

valkaari
19/04/2010, 23h08
Pas mal tout ça, par contre tu pourrais rajouter un truc pour nettoyer les splines intermédiaires.

César Vonc
19/04/2010, 23h11
Ah oui en effet, à force de regarder les détails j'en oublie les choses évidentes.

Je vais faire ça avec une case à cocher tiens, si jamais on veut garder les cerces intermédiaires (on sait jamais, ça peut servir).

oli_d
20/04/2010, 05h33
Super ! :efface:

+1 pour le nettoyage

Merci à Jean-Laurent pour les précisions sur les vecteurs :wink:

César Vonc
20/04/2010, 12h57
Et voilà !

http://img63.imageshack.us/img63/4296/lissagecercemenu.png

Avec un lien direct du script ici :

http://img405.imageshack.us/img405/6918/subdivisericone.png Télécharger (http://xt.serv2.free.fr/temporaire/Subdivision%20de%20cerce.CSC)

(Je joints le script à ce message par sécurité)
J'édite mon premier message du sujet en conséquence.

Je vais m'intéresser à d'autres méthodes de subdivision et je reviendrai vous embêter lorsqu'il faudra l'appliquer à un objet 3d. :)

clemz
20/04/2010, 18h53
println("MERCI CESAR ! :bounce: " );
println("MERCI OLI ! :bounce: " );
println("MERCI VAL ! :bounce: " );

super sujet , j'essaye justement d'apprendre à faire des scripts ( avec dialog popup etc )

je vous kiss vos fesses poillues ! :prie:

lolofedo
20/04/2010, 19h11
Juste pour vous dire, que ça à l'air vraiment super :poucehaut: , mais moi, j'ai strictement rien compris :idea:

César Vonc
21/04/2010, 12h04
Si t'as des questions en rapport à ce qui a été fait, Clemz, n'hésite pas à utiliser ce sujet.

En jouant, j'ai découvert un effet sympa lorsqu'on poussait l'intensité de la courbure à 200 ou 400 % avec 3 niveaux de subdivision puis en changeant le type de la cerce (en B-spline, par exemple) (sur l'image, en noir), à partir d'une simple cerce (en vert) :

http://img202.imageshack.us/img202/7513/demosubcerce2.jpg


Un petit rendu fait sur le vif :

http://img202.imageshack.us/img202/5517/demosubcerce3.jpg


On peut également créer une interpolation de forme entre les deux cerces en jouant avec l'Objet Déformation spline.

Cet effet pourrait être intéressant une fois que le script sera appliquable sur un objet 3d pour perturber une surface (aux modèles organiques, notamment).

clemz
23/04/2010, 13h18
:lol: excellent ton test :)

ouep pas de soucis si j'ai des questions ;) . en fait c'est surtout sur la création des interfaces 'popup' ("modal" ou "non modal" que je recherche ("non modal" j'ai pas réussi d'ailleur GetDialog() au lieu de GetModalDialog() ..il me met une erreur du genre parameter "super()" manquant un truc comme ça ... là je sèche encore ) .

:odile:

valkaari
23/04/2010, 13h59
vi la fonction super() c'est la fonction qui est appelé quand tu crées l'objet. C'est une fonction qui porte le même nom que ta class.

mais j'ai pas compris encore comment faire une fenêtre modal en coffee. (faire la même chose qu'en c++ fonctionne pas)

clemz
23/04/2010, 14h31
salut Val :)
ha ok mais cette fonction super() il en a pas besoin en fenêtre modal ?
(petite explication pour ceux qui connaissent pas 'modal' et 'non modal' : modal -> c'est quand la fenetre popup ouverte empêche tout autre action sur d'autres fenêtres ..il attend une instruction sur la fenêtre ouverte avant de la fermer et de redonner la main
et donc non modal -> c'est une fenêtre qui peut être mise de coté et donc laisser l'utilisateur travailler ailleur .. )

:odile:

valkaari
23/04/2010, 15h25
normalement non mais j'ai remis exactement ce que j'avais en c++ pour mon plug et en coffe ça ne fonctionne pas.

soit il manque un truc soit on ne peux pas faire de fenêtre non modale en script coffee. (dans un noeud ou dans un tag ou en script)

par contre en plugin coffee c'est surement possible.

Jean-Laurent
23/04/2010, 17h46
par contre en plugin coffee c'est surement possible.


Je sais que c'est la définition de surement mais dans le doute je confirme. :wink:
En plugin coffee on peut faire des fenêtres non modales.
Il y a des exemples dans le SDK.

Sinon, xs_yann m'avait passé un plugin de modale à non modale et je peux retrouver l'exemple au besoin.
Je ne savais même pas qu'on pouvait faire une fenêtre en dehors d'un plugin coffee. :oops:

valkaari
23/04/2010, 18h53
vi puis c'est comme en c++ ça fonctionne direct. Par contre dans un script (ce qui me parait normal) ça n'a pas l'air de fonctionner.

Puis pour le noeud ou le tag coffee je vois pas l'intérêt de créer une fenêtre puisque c'est exécuté à chaque frame.

oli_d
24/04/2010, 12h33
Puis pour le noeud ou le tag coffee je vois pas l'intérêt de créer une fenêtre puisque c'est exécuté à chaque frame.


je plussoie et de toute façon avec les DU on a déjà largement de quoi faire.

Je veux pas avoir l'air d'insister, mais de toute façon à mon avis le COFFEE en a plus pour très long avec ce qui se répare du côté du python. Si Maxon à racheté Py4d, c'est qu'ils ont sûrement l'intention de l'intégrer dans la prochaine version, et vu tous les avantages du python sur le COFFEE cela m'étonnerait qu'ils continue à développer le COFFEE...

César Vonc
24/04/2010, 21h31
Je me retrouve face à un problème : j'aimerais modifier la position des points ajoutés par la subdivision.

Pas de problème à priori lorsque les points sont dans la même direction, ça fonctionne bien en changeant la longueur du vecteur.

Le soucis est lorsque le point a une direction différente, j'ai beau additionner, multiplier la direction (variable dir) par des trucs pour comprendre le résultat, mais je ne comprends vraiment pas comment elle fonctionne.

J'aimerais obtenir l'image de droite à partir de celle de gauche :

http://img202.imageshack.us/img202/2428/image2xg.png

Je pensais à obtenir la direction de la tangente du vecteur vec, la coller à la direction du point que je veux bouger et additionner une longueur que j'ai déjà définie (on va l'appeler d).

Le point à définir est celui lorsque (m==2).

Ici, la variable subd = 4.

(Je colle uniquement le bout de code qui se charge de la position des points subdivisés).

for(m=1;m<subd;m++) // Boucle dans la boucle pour les points que l'on a créé
{
var x;
if(n==cnt-1) x=0; // Si on arrive sur le dernier point de l'ancienne cerce, il faut prendre le premier.
else x=n+1; // Pour la soustraction, sinon on prend n+1
var v;
if(m==1) v=3;
if(m==2) v=4;
if(m==3) v=4.5;
var vec = (pts[x]-pts[n]); // Valeurs des deux anciens points au milieu desquels on a inséré.
var dir = vnorm(vec); // Direction du vecteur.
var len = vlen(vec)/v; // Longueur du vecteur.
var pos = len*dir*m + pts[n]; // On rajoute au point [n] en fonction du point intermédiaire.
copie->SetPoint(i+m,pos);
}

J'espère ne pas manquer de précision.

oli_d
25/04/2010, 06h02
Salut,

Il faut utiliser la fonction vcross(v1,v2) qui te permets de récupérer un vecteur perpendiculaire au plan formé par v1 et v2.

Dans ton cas, pour le premier vecteur il faut récupérer la direction le sens (j'apprends !) de la droite entre le point précédent et le suivant. Pour le second vecteur dans l'exemple ci dessous j'ai récupéré l'axe Y, ainsi mon point va bouger sur l'axe XZ (pour autant que les deux points soient à la même altitude)


var distance = 40;

var cerce = doc->GetActiveObject();

var cnt = cerce->GetPointCount();

var pt0 =cerce->GetPoint(0); //point précédent à adapter en fonction de tes besoins
var pt1 =cerce->GetPoint(1); //point à bouger
var pt2 =cerce->GetPoint(2); //point suivant

var vec = cerce->GetMg()->GetV2(); //récupération de l'axe Y de l'objet

var dir = vnorm(pt0-pt2);//sens (direction) entre pt0 et pt3
dir = vcross(dir,vec); // perpendiculaire du plan formé par les deux droites
pt1 = pt1 + dir * distance; // on ajoute au point la distnce dans le sens de notre vecteur

cerce->SetPoint(1,pt1);
cerce->Message(MSG_UPDATE);

Jean-Laurent
25/04/2010, 08h50
C'est pour ça qu'utiliser les bons mots est important, même si c'est pas toujours facile. :wink:
J'ai parfaitement compris la réponse (nocturne :o) d'Oli_D mais du coup je ne sais pas si c'est bien ce que tu veux faire. :?:

Déplacer un point perpendiculairement à l'axe des deux points qui l'encadrent.
En changeant le signe de la distance on peut changer le sens du déplacement mais il se fera toujours perpendiculairement au segment formé par les deux points qui l'encadrent et dans le plan de ta cerce. :?:

Si c'est ça la méthode d'Oli_D marche très bien. :poucehaut:

Au passage pour bien comprendre ce qu'est "vcross" . C'est une fonction qui se nomme "produit vectoriel".
En math un nombre "classique" seul est appelé un "scalaire". Ex: 2, 16, 125 , PI = 3.14... etc ...
Et plusieurs nombres peuvent construire un vecteur: V = (2,5,6)

Un produit c'est une multiplication. 3*6 est un produit.

Avec les vecteurs il existe deux opérations très utiles qui ressemble à une multiplication.

Le produit scalaire: On multiplie deux vecteurs et ça donne un scalaire, donc un simple nombre.
C'est simplement la norme (longueur du premier vecteur) multipliée par norme de la projection du second vecteur sur le premier.
Deux cas particuliers intéressants:
- Si les vecteurs sont parallèles. V1.V2 = V1.V2 (on note les vecteurs en gras et leur longueur normalement)
- Si ils sont perpendiculaires. V1.V2 = 0
(cas général V1.V2 = V1*v2*cos(angle)

C'est donc un test facile pour vérifier si des vecteurs sont perpendiculaires ou pour évaluer l'angle entre deux vecteurs.

Le produit vectoriel: C'est celui qu'on utilise ici.
On le nomme comme ça car en multipliant deux vecteurs on obtient un autre vecteur. v1*v2 = v3
Le vecteur obtenu est perpendiculaire au deux autres comme le dit Oli-D, sa longueur est celle de l'aire du parallélogramme formé par les deux vecteurs soit V1*V2*sin (angle) et son sens dépend de l'ordre du produit vectoriel, on utilise la règle de la main gauche dans C4D et celle de la main droite en math ou dans d'autres logiciels.

Donc vcross(v1,v2) = - vcross(v2,v1) , l'ordre a de l'importance, il ne change pas la direction mais le sens.

Le produit vectoriel permet de vérifier rapidement si deux vecteurs sont colinéaires (parallèles), si c'est le cas il est nul.
Si par contre les deux vecteurs sont perpendiculaires il est maximal et vaut:
v1*v2 = 0

oli_d
25/04/2010, 09h36
J'ai parfaitement compris la réponse (nocturne :o)

pas nocturne, matinale ! :mrgreen:

Merci Jean-Laurent pour toutes ce précisions :poucehaut: Je pense qu'il serait bien de les regrouper sur ton sujet Math, non ? Quand je pense comme j'ai galéré (et comme je galère encore un peu) avec ces vecteurs et ces matrices.

Je ne sais pas si j'ai vraiment répondu à la question de César en fait ?

César Vonc
25/04/2010, 13h52
Merci pour toutes ces informations. :)

J'ai presque réussi à faire ce que je voulais avec le script d'Oli_D, sauf qu'en choisissant la perpendiculaire du plan entre mon vecteur et l'axe Y, la cerce se devait d'être plate et sur le plan XZ pour que ça fonctionne correctement.

Du coup au lieu de prendre l'axe Y, j'ai pris la perpendiculaire du plan entre mon vecteur et celui entre les points autour des points de mon vecteur... je ne sais pas si je me fais bien comprendre. :lol:
Avec une bonne dose de conditions derrière pour le premier et dernier point, ça passe.

Soit :
var veco = vnorm(vcross(vnorm(pts[supo]-pts[info]),vec)); // Perpendiculaire du plan du vecteur vec et celui des points autour.
Au lieu de :
var veco = copie->GetMg()->GetV3();

Peut-être y a-t-il un moyen plus simple avec la normale du point ou la tangente...


J'aurais juste besoin de savoir si on peut mettre le sens du vecteur en valeur absolue et, une chose encore plus simple, comment calculer le sinus de 60°, car sin(60) ne me donne pas le bon résultat.

Je vous montrerai le résultat avec le script complet si au final tout fonctionne bien (ce qui devrait donner la fractale de Koch).

Jean-Laurent
25/04/2010, 16h06
pas nocturne, matinale ! :mrgreen:


Toutes mes excuses alors. :mrgreen:
Mais le mythe du programmeur nocturne en prend un coup.



J'aurais juste besoin de savoir si on peut mettre le sens du vecteur en valeur absolue et, une chose encore plus simple, comment calculer le sinus de 60°, car sin(60) ne me donne pas le bon résultat.


La valeur absolue existe. Il me semble que c'est tout simplement la fonction abs() ou un truc du genre.
Sinon sin(60°) c'est racine de 3 divisé par deux. Un classique. :wink:
Cf le cercle trigonométrique, bien utile.

Si tu as un problème c'est le passage des degrés aux radians.
Il y a une simple proportionnalité entre les deux.
180° = PI
90° = PI/2 etc ....

Et donc sin(60°) = sin (PI/3) = sqrt(3)/2

Il existe aussi une fonction qui converti les degrés en radians et réciproquement.

César Vonc
25/04/2010, 17h34
Ah en effet, j'avais pas pensé aux radians.

Pour la valeur absolue, abs() ne semble pas fonctionner, ni fabs().

Je peux toujours multiplier par -1 quand le résultat est négatif, mais j'aimerais autant alléger le code dès que possible.

Jean-Laurent
25/04/2010, 18h19
Pour la valeur absolue, abs() ne semble pas fonctionner,


Curieux. :o
J'ai vérifier dans le SDK et c'est bien abs(), c'est un classique.
Il y a aussi Degrees() et Radians()

Sinon on peut toujours tricher aussi avec la racine du nombre élevé au carré, ce qui revient au même.
sqrt(a*a) = |a|

Mais la valeur absolue devrait marcher.

César Vonc
25/04/2010, 18h46
Ah si, en fait ça fonctionne sauf que la fonction ne se colorait pas dans la syntaxe, bizarrement. Au temps pour moi.

Je croyais que c'est ce qui posait problème, mais en fait non, j'ai bien un autre soucis par rapport à mon :

var veco = vnorm(vcross(vnorm(pts[supo]-pts[info]),vec));

Le problème c'est que si la courbe est une ligne droite constitués de plusieurs points, le plan ne peut pas se définir étant donné que les deux vecteurs sont confondus.

Est-il possible de récupérer la droite normale d'un point d'une cerce ?

Jean-Laurent
25/04/2010, 21h10
C'est plutôt normal. :mrgreen:
Si tous les points sont alignés c'est une droite et il n'y a pas de plan défini. Il faut en choisir un.
Je n'ai toujours pas bien compris dans quelle direction tu veux déplacer tes points. :?:

Par droite normale d'un point d'une cerce, tu parles de la perpendiculaire à la courbe en un point donné c'est ça?
Vu que tu as des points discrets (qui ne sont pas continus) il faudra faire une approximation.

César Vonc
25/04/2010, 22h51
Pour la direction des points, je tâtonne justement à son sujet, je l'ai pour l'instant défini de cette manière :

http://img231.imageshack.us/img231/5005/direction.png

Ce que j'expliquais tout à l'heure peut se résumer avec ce schéma qui, j'espère, est plus clair.


C'est ce qui merde lorsque les points sont alignées, oui. L'ennuie si je définis de manière stricte quelle direction prendre lorsqu'ils sont alignés, c'est que cette direction peut être trop différente de celles des autres points de la cerce, comment expliquer...

Dans le calcul actuel de la direction, celle-ci suit une certaine logique en fonction du point précédent, si je mets tout d'un coup une direction stricte, comme par rapport à l'axe de la cerce, ce ne sera pas joli.

L'autre problème avec la méthode actuelle, c'est que parfois la direction part dans le mauvais sens :

http://img714.imageshack.us/img714/2203/image6it.png

C'est pourquoi j'avais besoin de la valeur absolue, mais je n'arrive pas à l'appliquer correctement (abs(vnorm(vec)) ne marche pas).
Cette différence de sens vient sans doute de la soustraction entre le points n et n+1, mais là non plus, abs() ne fonctionne pas (ne marche-t-il que pour des nombres scalaire ?).


Toute proposition pour définir cette fichue direction est bonne à prendre.



var n=0; // On va utiliser plusieurs iterateurs.
var m;
for(i=0;i<new_cnt;i+=subd) // Boucle avec le pas de la valeur de subdivison.
{
copie->SetPoint(i,pts[n]); // Le point i correspond à notre ancien point n que l'on récupère de notre tableau.
for(m=1;m<subd;m++) // Boucle dans la boucle pour les points que l'on a créé
{
var x;
if(n==cnt-1) x=0; // Si on arrive sur le dernier point de l'ancienne cerce, il faut prendre le premier.
else x=n+1; // Pour la soustraction, sinon on prend n+1
var v;
if(m==1) v=3;
if(m==2) v=4;
if(m==3) v=4.5;

var vec = (pts[x]-pts[n]); // Valeurs des deux anciens points au milieu desquels on a inséré.
var dirvec = vnorm(vec); // Direction du vecteur.

var supo; // Conditions pour le premier et denier point.
var info;
if (n==cnt-1) supo=n;
else supo=n+1;
if (n==0) info=0;
else info=n-1;
if (n==0) supo=n+2;

var veca = (pts[supo]-pts[info]);
var veco = vnorm(vcross(vnorm(veca),vnorm(vec))); // Perpendiculaire du plan des vecteurs vec et celui des points autour.


var len = (vlen(vec)/v); // Longeur du vecteur.
var pos = len*dirvec*m + pts[n]; // On rajoute au point [n] en fonction du point intermédiaire.

if(m==2)
{
var dir = vcross(dirvec,veco);
var distance = (vlen(vec)/3)*sin(PI/3); // Distance ajoutée.
pos = pos + dir * distance;
}


copie->SetPoint(i+m,pos);
}
n++; // On rajoute 1 à n.
}

Jean-Laurent
26/04/2010, 08h16
Le problème est bien mieux posé. On peut y réfléchir plus sérieusement du coup. :poucehaut:

Sinon, oui, la valeur absolu ne peut marcher que pour un scalaire qui du coup sera toujours positif.
La définition c'est |x| = x si x>=0 et |x| = -x si x<0

Pour un vecteur on ne peut plus l'appliquer. La norme d'un vecteur n'est jamais négative. Quant aux coordonnées elles dépendent du repère de référence.

edit: Ok, j'ai compris ce que tu as fais mais toujours pas totalement ce que tu veux obtenir au final. :wink:

Au passage dans: var veco = vnorm(vcross(vnorm(veca),vnorm(vec)))
Il y a des vnorm qui ne servent à rien c'est vnorm(veca) et vnorm (vec)

vnorm te donne le vecteur normalisé. C'est à dire exactement le même vecteur mais avec une norme (longueur) de 1.
donc si tu fais vnorm(vcross(veca,vec)) c'est la même chose.
Puisque tu normalises à la toute fin le produit vectoriel.
Comme en plus tu refais un vcross par la suite. Tu n'as même pas besoin du vnorm ici.

Donc veco = vcross(veca,vec) suffit. Le vecteur donnera le bon sens et la bonne direction, c'est juste que ça longueur ne sera pas de 1.
Mais on s'en fiche. :wink:
C'est dir qu'il faut normaliser, donc dir = vnorm ( ...)
Le produit vectoriel de deux vecteurs normalisés ne donne pas un vecteur normalisé.

La notion d'esthétique de la courbe n'est pas facilement conciliable avec celle d'automatisation. On en a déjà parlé avec les toiles d'araignées. Mais si il y a plusieurs possibilités il faut faire un choix.
Pour le sens tu voudrais que le déplacement soit toujours "extérieur" à courbe c'est ça.
Ta courbe est forcément fermée donc c'est possible mais selon le type de courbe ça peut poser problème (chevauchement etc ...)

Si tu pouvais redonner un exemple rapide sous Paint de courbe complète, fermée , avec les points avant et après déplacement. :oops:

César Vonc
26/04/2010, 12h28
Je comprends mieux la fonction vnorm, à présent.


Pour ce qui est de la direction lorsque les points sont alignés, j'ai trouvé une solution qui consiste à définir mon vecteur veca avec les points n-q et n+q jusqu'à ce que mes deux vecteurs vec et veca n'aient plus la même norme, en ajoutant 1 à q à chaque passage de la boucle.

S'il sont égaux coûte que coûte (lorsque la cerce n'est qu'une ligne de points alignés), alors j'impose une direction.


Pour ce qui est du sens, apparemment celui-ci change en fonction de l'angle entre mon vecteur vec et veca :

http://img101.imageshack.us/img101/1273/image1hs.png

Tel quel, le risque de chevauchement est moindre, mais j'aimerais quand même pouvoir ordonner une direction dans le même sens.

Peut-on mesurer l'angle entre deux vecteurs ?

Je peux essayer avec la formule que tu as indiquée plus haut : V1.V2 = V1*V2*cos(angle)
Ce qui donnerait quelque chose comme : angle = acos((V1.V2)/(V1*V2))

Mais s'il existe une fonction tout prête, je suis également preneur.

Jean-Laurent
26/04/2010, 19h01
Pour ce qui est du sens, apparemment celui-ci change en fonction de l'angle entre mon vecteur vec et veca :


Tout à fait. C'est le principe du produit vectoriel. Et pour mieux comprendre comment ça marche je développe un peu.

vcross(V1,V2) réalise le produit vectoriel des vecteurs V1 et V2 et le résultat sera donc un troisième vecteur V3 perpendiculaire aux deux premiers. V3 = V1 *V2

La longueur de V3 est donné par V1*V*sin(angle) mais ce qui t'intéresse ici c'est le sens.
On utilise la règle de la main gauche, C4D étant un logiciel main gauche.

Tu lèves le pouce de la main gauche comme pour faire du stop. Et tu places ce pouce suivant V1.
Puis tu déplies complétement l'index de la main gauche jusqu'à environ un angle de 90° par rapport au pouce (en principe tu peux pas plus) :mrgreen:) un peu comme pour mimer un pistolet. Tu diriges l'index dans une direction proche de V2.
Si V2 et V1 sont à plus de 90 ° ça marche aussi mais ne te déboite pas un doigt. Par contre si l'angle dépasse 180° tu ramènes ça à un angle inférieur en tournant la main. 190° c'est 170°.

Enfin tu déplies lentement le majeur (sans commentaire :mrgreen:) jusqu'à ce qu'il soit à 90° avec les deux autres doigts et il te donne la direction de V3 résultat de ton produit vectoriel.

Il est facile du coup de se rendre compte que vcross(V1,V2) = - vcross(V2,V1), le sens est inversé.

Sur ton schéma on voit effectivement que le sens est inversé quand on cesse de tourner vers la droite et qu'on tourne vers la gauche.
Autrement dit, quand veca et vec changent de position relative. L'un qui était à l'extérieur passe à l'intérieur.

Il existe une fonction qui mesure les angles mais je ne sais pas si elle prend en compte l'orientation. (-10°, +10° etc ....) c'est MinimizeAngle().
Tu as bien le SDK pour regarder les fonctions possibles ?

Sinon le produit scalaire ne t'aidera pas car il te donnera l'angle entre les vecteurs (par exemple 20°) mais ne de dira pas si c'est +20° ou -20°. Ce qui ici nous intéresse.

Il faut donc simplement que tu vérifies si la courbe tourne vers la droite ou vers la gauche.
Et c'est justement vcross qui le fait. :wink:
En vérifiant si le produit vectoriel est dans le même sens que les précédent. Autrement tu l'inverses.
Tu peux vérifier si veco est toujours dans le même sens avec le produit scalaire du nouveau veco et de l'ancien.
Si il est positif, c'est gagné, négatif il faut inverser veco.
Mais il y a peut-être plus simple. :roll:

Pour rappel sur le produit scalaire, (la fonction doit exister en coffee) :
V1.V2 = V1.x*V2.x + V1y*V2.y+V1.z*V2.z

César Vonc
26/04/2010, 21h44
Sinon le produit scalaire ne t'aidera pas car il te donnera l'angle entre les vecteurs (par exemple 20°) mais ne de dira pas si c'est +20° ou -20°. Ce qui ici nous intéresse.
Ah ! Voilà donc pourquoi ça ne fonctionnait pas.


Bien expliqué, tout ça, (ça me rappelle un cours d'électricité sur le champ magnétique, cette histoire de doigts).


Pour vérifier s'ils sont dans le même sens, tu vérifies que vcross est plus grand que l'ancien vcross ?

Parce qu'en faisant ça (if (vcross de l'ancien < vcross du nouveau)...), j'ai une erreur d'incompatibilité (en soit c'est logique si je compare deux vecteurs et non leur sens).


Édition : Pour ce qui est des fonctions, j'ai vraiment du mal à me servir du SDK ; celui de coffee est quasiment vide, et celui du C++ n'est pas totalement compatible (il y a bien la fonction VectorAngle(), mais celle-ci ne semble pas fonctionner en coffee).

Je crois qu'après ce script, je passerai au Python pour le retranscrire (j'ai du réveiller Oli_D, là. :lol:)

Jean-Laurent
26/04/2010, 21h58
Bien expliqué, tout ça, (ça me rappelle un cours d'électricité sur le champ magnétique, cette histoire de doigts).


:mrgreen:

C'est exactement ça. :poucehaut:
A la base c'est pour les physiciens et ce genre de chose que le calcul vectoriel a été développé.
Tu te souviens sans doute de la force de Laplace F = I dl *B

Mais pour revenir à nos moutons.
Pour vérifier qu'ils sont dans le même sens, c'est ce que je proposais.
Vérifier le produit scalaire des produits vectoriels. :nono:
Il y a peut-être plus simple mais là c'est l'idée qu'il me viendrait. :roll:

Si la fonction produit scalaire n'existe pas il faut la créer, c'est pas trop dur.
C'est la définition que j'ai remis dans le message précédent.
Un produit scalaire positif signifie un angle entre les vecteurs inférieurs à 90° ce qui est certainement le cas si ils sont dans le même sens et que leurs directions sont voisines.
Un produit scalaire négatif signifie que les vecteurs ont un angle supérieur à 90° donc pointent avec des sens opposés.

César Vonc
27/04/2010, 11h29
Tu ne serais pas prof de physique, par hasard ?

Problème résolu ! :)

J'avais commencé à faire ce produit scalaire, mais je me suis rendu compte que le script devenait une vraie usine à gaz pour définir le vecteur précédent et le comparer, du coup je me suis demandé pourquoi comparer avec le précédent alors que ma direction doit toujours avoir même sens, soit un sens absolu.

J'ai donc fait une sorte de produit scalaire sans autre vecteur (enfin, avec un vecteur fictif (1,1,1)), bref disons que j'ai additionné les valeurs du vecteur vcross (j'ai pris le vecteur vcross normalisé pour éviter créer une nouvelle variable mais je ne pense pas que ça change grand chose) et comparé le résultat à zéro.

S'il l'est, j'inverse le signe de ma distance pour changer le sens... et ça marche ! Toutes les directions sont dans le bon sens.

http://img691.imageshack.us/img691/4620/enfing.png

Deux autres petits exemples avec plusieurs niveaux de subdivision qui agissent comme une fractale, dont le flocon de Koch (à partir d'un triangle), et un exemple à partir d'un tracé quelconque :
http://img691.imageshack.us/img691/3402/flocondekoch.png http://img697.imageshack.us/img697/1011/exemplefractalekoche.png

Je vais intégrer tout ça dans mon script de départ et m'intéresser au Python avant de voir d'autres fractales.
En tout vas, grand merci à toi, Jean-Laurent.

Le bout de script, pour les curieux (ma cerce est la variable copie) :


var subd = 4; // Nombre de subdivisions

var vc = new (VariableChanged); if(!vc) return;
var cnt = copie->GetPointCount();

var ferme = (copie->GetContainer())->GetData(SPLINEOBJECT_CLOSED); // Je regarde si ma cerce est fermée.
var relie;
if (ferme == FALSE) relie = 1;
else relie = 0;

var pts = new(array,cnt); // Création d'un tableau pour les anciennes valeur de point.
var i;
for(i=0;i<cnt;i++) // Boucle pour récupérer tous les points dans ce tableau (boucle tant que i < nb de points, à chaque boucle i=i+1).
{
pts[i]=copie->GetPoint(i); // Je récupère les points.
}
var new_cnt = cnt + (cnt-relie)*(subd-1); // Pour chaque point sauf le dernier on rajoute nb de subdivision -1.
vc->Init(cnt,new_cnt); // Ajout des points à la cerce.
var ok = copie->MultiMessage(MSG_POINTS_CHANGED, vc);

var n=0; // On va utiliser plusieurs iterateurs.
var m;
for(i=0;i<new_cnt;i+=subd) // Boucle avec le pas de la valeur de subdivison.
{
copie->SetPoint(i,pts[n]); // Le point i correspond à notre ancien point n que l'on récupère de notre tableau.
for(m=1;m<subd;m++) // Boucle dans la boucle pour les points que l'on a créé
{
var x;
if(n==cnt-1) x=0; // Si on arrive sur le dernier point de l'ancienne cerce, il faut prendre le premier.
else x=n+1; // Pour la soustraction, sinon on prend n+1
var v;
if(m==1) v=3;
if(m==2) v=4;
if(m==3) v=4.5;

var vec = (pts[x]-pts[n]); // Valeurs des deux anciens points au milieu desquels on a inséré.
var dirvec = vnorm(vec); // Direction du vecteur.

var len = (vlen(vec)/v); // Longeur du vecteur.
var pos = len*dirvec*m + pts[n]; // On rajoute au point [n] en fonction du point intermédiaire.

if(m==2)
{
var veca;
var veco;

if (cnt==2) // S'il n'y a que de deux points dans la cerce.
{
veco = copie->GetMg()->GetV3(); // Imposer la direction.
}
else // Sinon, la calculer.
{
var supo; // Conditions pour le premier et denier point.
var info;
if (n==cnt-1) supo=n;
else supo=n+1;
if (n==0) info=0;
else info=n-1;
if (n==0) supo=n+2;

veca = (pts[supo]-pts[info]);

var q=0;
while (vnorm(veca)==dirvec) // Si les deux vecteurs se confondent...
{
q=q+1;
if (n!=cnt-1)
{ supo=supo+q; } else supo=1;
if (supo>cnt-2) supo=1;
if (n!=0)
{ info=info-q; } else info=2;
if (info<1) info=2;
veca = (pts[supo]-pts[info]); // Je redéfinis veca.
}
veco = vnorm(vcross(veca,vec)); // Perpendiculaire du plan des vecteurs vec et celui des points autour.
}

var dir = vnorm(vcross(vec,veco)); // Direction.
var distance = (vlen(vec)/3)*sin(PI/3); // Distance ajoutée.
if ( (veco.x+veco.y+veco.z)<0 ) distance=-distance; // Condition pour l'inversion du sens.
pos = pos + dir * distance; // Position.
}

copie->SetPoint(i+m,pos);
}
n++; // On rajoute 1 à n.
}

Jean-Laurent
27/04/2010, 11h41
:poucehaut:

Je me doutais bien qu'il y avait plus simple.
Même si je n'ai pas tout compris de ta solution. Ton code est pourtant très bien documenté. :prie:
Mais je manque un peu de temps pour me plonger dedans. :oops:
L'essentiel c'est que ça marche. Tu as vérifié si il n'y a pas de problème si la cerce n'est plus plane ?

La fractale est très sympa. :bave:
Si ma mémoire est bonne l'aire de cette fractale est finie (facilement démontrable) mais la longueur est infinie.

Bien vu pour la physique et le radotage. :mrgreen:

César Vonc
27/04/2010, 12h31
En gros, voici la ligne qui indique quand j'inverse le sens :

if ( (veco.x+veco.y+veco.z)<0 ) distance=-distance; // Condition pour l'inversion du sens.

J'avoue que je ne pensais pas que ça fonctionnerait... peut-être que je crois avoir compris et que j'ai eu un gros coup de bol. :lol:


En cas de cerce non plane, ça fonctionne bien sauf que c'est moins joli étant donné qu'il n'y a pas de lissage (la subdivision ajoute des points au milieu des segments, donc ils restent bien droits).

On pourrait avoir quelque chose de plus joli en bougeant les points originaux après chaque subdivision de Koch avec la méthode Catmull-Clark sans subdiviser (ou un truc dans le genre)... mais bon, une petite pause s'impose. ^^

Exemple avec une spirale :

http://img21.imageshack.us/img21/5810/spiralev.png

clemz
27/04/2010, 12h59
super Cesar :prie:

ton taff me fait penser au fameux 'L-system'

http://fr.wikipedia.org/wiki/L-System

impressionnifiant :odile:

César Vonc
27/04/2010, 17h10
Merci, ton lien me fait penser à ce sujet (http://www.frenchcinema4d.fr/forum/index.php?topic=21984.0), qui permet de générer des arbres de manière fractale.


J'ai mis à jour le lien en première page pour cette nouvelle version du script avec un menu déroulant permettant de choisir la Courbe de Koch comme type de subdivision.

http://img18.imageshack.us/img18/112/subdivisiondecerce.png


Pour la prochaine version, j'aimerais faire quelque chose de plus abouti, comme définir son propre motif fractal.

Est-il possible de créer une petite interface afin de pouvoir créer ce motif, en Python ?

Quelque chose comme la courbe éditable qu'on rencontre souvent dans les attributs des outils de c4d :

http://img52.imageshack.us/img52/4593/du1.png

Sauf qu'elle se déplace de gauche à droite, je voudrais qu'on puisse dessiner n'importe quel motif et récupérer les points ajoutés sous forme de coordonnées pour ensuite les exploiter. Est-ce faisable dans Python ?

valkaari
27/04/2010, 18h06
ha ben encore un taré dans le coin..


pour dessiner, pourquoi ne pas tout simplement prendre une spline ?


sinon impec ton script je me suis pas acharné dessus mais j'ai pas trouvé de bug

Jean-Laurent
27/04/2010, 18h42
Est-il possible de créer une petite interface afin de pouvoir créer ce motif, en Python ?


Une bonne question. Je dirais que oui à priori mais maintenant le comment. :nono:
En python je saurais faire. Avec le module TKinter. Et encore pas évident à mettre en place. Surtout si on veut une courbe avec des tangentes. Mais maintenant en Python de C4D ...
Pour les plugins coffee on pouvait créer une "userarea" dans laquelle on pouvait dessiner ce genre de chose et récupérer il me semble la position de la souris lors de click. Donc à priori ça devrait être possible ...

La courbe avec une fonction déjà toute prête ça serait encore mieux mais à mon avis il ne faut pas croire au père Noël.

César Vonc
28/04/2010, 16h30
En effet, je pourrais utiliser une cerce de base qui définirait le profil à répéter, sauf que ça compliquerait grandement la tâche de trouver dans quelle direction est orienté le profil puis en déduire la position relative à cette direction de chaque point, sans compter qu'ils peuvent être situés dans l'espace et non sur un plan comme c'est toujours le cas actuellement...

Ceci dit c'est sûrement faisable, (je viendrai vous embêter souvent).


En attendant, je complète la bibliothèque de fractales du script.
J'ai également ajouté une option permettant d'inverser le motif une fois sur x.

Pour illustrer tout ça, voici un exemple de la courbe du dragon avec une intensité modifiée, et deux exemples de la fractale de Lévy (ou courbe C), dont une avec un inversement de profil une fois sur trois (vous l'auriez deviné, bien entendu !) :

http://img203.imageshack.us/img203/7060/fractales1.png
La cerce de départ était une simple droite.

J'en ajoute encore quelques unes et je mets à jour le script (les variantes de fractales augmentent considérablement, surtout si on s'amuse à en mélanger plusieurs entre elles).

luxereau
28/04/2010, 16h37
Je suis ça en silence mais respect :prie:

Aurety
28/04/2010, 16h59
:shock: pareil en silence, d'ailleurs je peux pas parler y'a trop de bave, à peine écrire... :prie:

BerTiN03
28/04/2010, 18h51
C'est complétement barge, ton truc... oO' :prie:

Jean-Laurent
28/04/2010, 19h56
Très joli tout ça. :poucehaut:

César Vonc
28/04/2010, 22h11
Merci !

Je suis tombé sur un problème en parti lié à c4d.

Lorsqu'on pivote un vecteur sur un plan qui n'est pas un des plans de l'espace (XZ, XY, ZX), les coordonnées des points du vecteur sont arrondis à la précision de c4d, c'est à dire 0,001 unité.

Cette différence suffit à me poser problème lorsque je vérifie la colinéarité entre deux vecteurs :

if ( vnorm(V1) == vnorm(V2) )

Y aurait-il un moyen d'ajouter une marge de vérification de +/- 0,001 ?

J'ai essayé avec :

var precision = vector(0.001, 0.001, 0.001);
if ( (vnorm(V1) >= vnorm(V2)-precision) && (vnorm(V1) <= vnorm(V2)+precision) )

Mais ça m'indique une erreur de données incompatibles entre Vecteur et Vecteur...

Loriel
28/04/2010, 22h11
:shock:

gaff
28/04/2010, 23h55
Tiens, voilà ta petite pilule…
après les monsieurs avec un blouse blanche vont venir…
tu vas être gentil, éteindre ton ordinateur et faire une petite pause hein! :love:

tarlack
29/04/2010, 00h21
calcule la valeur absolue du produit scalaire de vnorm (V1) et vnorm (V2), et s'il est compris entre 0.99 et 1.01 c'est bon (à cause des arrondis et de la propagation lors des additions, tu peux pas etre sur que ce soit entre 0.999 et 1.001, donc on prend large). sinon pour ton incompatibilité de type, ils n'ont juste pas définis d'operateur <= et >= qui marchent sur des vecteurs et qui renvoient des booleens, ce qui est plutot normal vu que la condition peut etre vérifiée pour une des composantes du vecteur mais pas une autre...
bon cela dit un type de flottant qui soit limité à une précision de 0.001, ca me parait assez gênant...

valkaari
29/04/2010, 03h42
et un truc du type

if (vlen(vcross(v1,v2)) <= 0.001)

Jean-Laurent
29/04/2010, 13h58
if (vlen(vcross(v1,v2)) <= 0.001)


+1

Sinon on ne peut pas comparer des vecteurs comme le précise tarlack. On peut comparer leur norme, leurs angles par rapport à d'autres etc ...

flahaut
29/04/2010, 14h12
Tiens, voilà ta petite pilule…
après les monsieurs avec un blouse blanche vont venir…
tu vas être gentil, éteindre ton ordinateur et faire une petite pause hein! :love:

ça va pas , non ? pas de petite pause ! si leurs neurones ne surchauffent pas , ils vont tomber malades , tu ne voudrais pas être responsable ? :mrgreen:
je suis ce sujet avec beaucoup d'interêt , utilisant apophysis fractales , entre autres ( mon court métrage " où je vais " en est bourré )
ça me rappelle d'ailleurs pas mal de maths qui ne m'ont jamais servi et qui me prenaient la tête. :coup:

César Vonc
29/04/2010, 23h48
Ah merci, ça marche impec !

Je me rends compte que les fractales sont beaucoup plus attractives que le lissage Catmull-Clark qui était censé être le but final du script. :lol:


J'ai ajouté la courbe du triangle de Sierpinski, ainsi qu'une nouvelle option permettant de changer le sens du motif chaque subdivision sur x (par exemple une fois sur deux) (que j'ai maintenant appelé le nombre d'étape, pour éviter de me tromper avec les subdivisions de la cerce proprement dites du script)

http://img338.imageshack.us/img338/5246/fractale3.png


Une autre faite en jouant avec les options :

http://img707.imageshack.us/img707/8059/fractale2.png

http://img32.imageshack.us/img32/135/subdivicercemenu.png

J'ai également la valeur de la précision qui joue sur la comparaison de mes deux vecteurs, il ne devrait pas y avoir besoin d'y toucher.

Les options n'affectent pour l'instant pas le lissage Catmull-Clark, mais toutes les autres fractales du menu déroulant et permettent de varier davantage leur aspect.


Le script a été mis à jour, vous pouvez le télécharger via le premier message du sujet, ou directement en pièce jointe à ce message.

Je vais maintenant voir pour la création d'un motif personnalisé.

BerTiN03
30/04/2010, 07h03
Vous êtes tous extrêmement débiles, c'est génial ! :poucehaut:

flahaut
30/04/2010, 12h57
désolé pour cette question très bas niveau :oops: : votre fichier .CSC apparait sur mon PC comme un script corel car j'ai un vieille version de corel photopaint que je conserve car il permet par script justement d'appliquer des effets automatiquement à chaque image d'un fichier avi .
comment puis-je utiliser votre fichier dans C4D ?

shtl
30/04/2010, 13h42
Dans la r11.5, un dossier d'utilisateur est créé. Si tu n'as pas repéré son emplacement, tu peux le retrouver en lançant les prefs générales, puis en bas du premier onglet, "common", un bouton "open folder". Dedans tu dois avoir un sous-dossier "librairy", et enfin un sous-dossier "scripts". Idéalement tu peux le placer à cet emplacement. Ainsi au prochain lancement de c4d, ton script sera chargé, et disponible dans les Plugins/User Scripts.

Notes aussi que ce script préfère les splines/cerces créés manuellement, et sinon ne trace que les 11 derniers segments bien que tout les points soient en place. Donc avec une spline "dessinée" ça marche nickel :poucehaut:

flahaut
30/04/2010, 14h00
merci, j'aurai dû penser à ce sous dossier " script " que je connais mais n'utilise jamais.

il est vrai que je suis plus à l'aise dans xpresso que dans les lignes de code... :coup:

César Vonc
30/04/2010, 14h54
Notes aussi que ce script préfère les splines/cerces créés manuellement, et sinon ne trace que les 11 derniers segments bien que tout les points soient en place. Donc avec une spline "dessinée" ça marche nickel :poucehaut:


Hmm, je ne comprends pas bien, normalement toute la cerce est affectée, pourrais-tu me montrer une capture de ce qui ne va pas ?

J'ai peut-être oublié de dire que le script ne marche que pour une cerce continue. Si c'est un ensemble de plusieurs courbes, ça merdera.


Autre problème que je viens de voir, lorsqu'une cerce dessinée est subdivisée par le script, sa taille en axe Z augmente (passe de 0 à 0,003, par exemple), ce qui fait que la subdivision d'après prend ça comme du relief, et après plusieurs passages, le résultat est un peu bordellique, comme une sorte d'extrusion aléatoire dans l'axe Z.

Ce problème vient de la précision de c4d dont je parlais plus haut, qui est limitée à 0,001. Si vous modifier l'échelle de votre cerce dessinée à main libre, vous verrez qu'elle gagnera en taille Z de la même façon (dans le cas où elle est dessinée directement dans la vue 3d).

Donc je ne vois vraiment pas comment corriger ce soucis si ce n'est de vous conseiller d'opérer dans une échelle supérieure, afin que la précision soit accrue.
Mais ça ne fait que retarder le problème qui reviendra après un certain nombre de passages (on peut aussi le retarder en augmentant la valeur de l'approximation, (que j'ai (mal) nommé en Précision de profondeur, dans l'interface)

valkaari
30/04/2010, 15h40
je suis pas certain que c4D soit limité à 0.001, je dirais plutôt que c'est son affichage qui a une précision au millième d'unité.

si tu fais un calcul dans coffee, tu constate qu'il peut aller à 0.000 001

Peux être utiliser des tableaux au lieu des splines intermédiaires.

César Vonc
01/05/2010, 13h15
Pas bête, ça pourrait arranger la chose.
Je reviendrai sur ce problème un peu plus tard.

Actuellement je suis assez content car le motif personnalisé fonctionne ! :)

Avec certes pas mal de contraintes, (motif 2d, sur le plan XY, aligné sur l'axe X...) mais j'y travaille.

http://img260.imageshack.us/img260/4532/motifperso.png

Au centre : le motif perso, en blanc : la cerce de base, en rouge : étape 1, en vert : étape 2.


Je voudrais ajouter une option pour gérer l'intensité de manière hasardeuse. Peut-on créer une variable aléatoire en coffee ?


Pour ceux qui chercheraient à faire une interface en coffee (je pense notamment à Clemz), j'ai trouvé un excellent site japonais qui explique tout (on comprend grâce aux images) :
http://villager-and-c4d.cocolog-nifty.com/blog/2009/08/c4d--02-7a26.html

Jean-Laurent
01/05/2010, 13h22
Je voudrais ajouter une option pour gérer l'intensité de manière hasardeuse. Peut-on créer une variable aléatoire en coffee ?


Pour du vrai aléatoire ça se passe là en bas de page:
http://www.frenchcinema4d.fr/forum/index.php?topic=21984.0

Les liens vers les fichiers sont toujours valides apparemment.

oli_d
01/05/2010, 13h59
Je ne sais pas ce que vous avez contre la classe Random du coffee, mais chez moi cela fonctionne plutôt bien. Il faut juste lorsque l'on a une boucle bien s'assurer que le Init() soit bien à l'extérieur

Exemple pour tirer 20 nombres aléatoires entre 0 et 1:

var random = new(Random);
random->Init(time());
var i;
for (i=0;i<20;i++)
{
println(random->Get01());
}

exemple pour des nombres de 1 à 10 :


var random = new(Random);
random->Init(time());
var i;
for (i=0;i<20;i++)
{
println(int(random->Get01()*11));
}



idem pour des nombres entre 10 et 20 :

var random = new(Random);
random->Init(time());
var i;
for (i=0;i<20;i++)
{
println(int(random->Get01()*11)+10);
}


et pour finir en écrivant une fonction qui tire un nombre aléatoire entre min et max (hors gestionnaire de script, sur un tag par exemple):

nbre_alea(min,max,random)
{
var dif = max-min+1;
return(int(random->Get01()*dif)+min);
}

main(doc,op)
{
var random = new(Random);
random->Init(time());
var i;
for (i=0;i<20;i++)
{
println (nbre_alea(5,15,random));
}
}

Il y a également un aléatoire gaussien regarde dans la doc

Jean-Laurent
01/05/2010, 14h23
Pour avoir souvent utilisé le Random de C4D il ne me satisfait pas du tout. :wink:

Tout est question de goût. Mais je développe un peu.
Pour tirer un nombre au hasard il ne présente aucun problème.
Pour en tirer une centaine non plus.
Jusqu'à 1024 ça va encore. :wink:

Mais pour une animation qui fait appel régulièrement à un phénomène aléatoire ça ne va plus du tout à mon avis.

Le random de C4D dépend de l'initiation de départ.
Ainsi random(224) donnera toujours le même résultat.

L'ennui c'est que les valeurs du random sont trop prévisibles en fonction de cette initiation.
random (225) et random (226) sont trop proche de random (224) (je simplifie la notation)

Avec random (time) on a l'impression d'être à l'abri, mais le temps évoluant régulièrement si on fait appel régulièrement à cette fonction, les valeurs deviennent prévisibles. Et si on n'y fait pas appel elles bouclent rapidement.
Je m'en suis vite rendu compte sur tout un tas de petits programmes que j'avais fait nécessitant ce random.

C'est pourquoi j'ai parlé de vrai random, même si celui de C4D peut dépanner, il est loin d'être très efficace.

César Vonc
01/05/2010, 23h45
Parfait !

J'ai pris la version de Oli_D, qui suffit amplement à ce que je veux faire, étant donné que la source n'est pas seulement le temps, mais également le nombre de passages, l'indice du point... ce qui augmente le hasard.

Le résultat obtenu est intéressant car on obtient des fractales imparfaites, de quoi leur donner un air plus naturel :

http://img594.imageshack.us/img594/6411/fractaleimparfaite.png

tarlack
02/05/2010, 21h53
un bon moyen pour initialiser un generateur à partir d'indices consécutifs (tes points) c'est de faire des overflows. par exemple : Init (idx_point * 37909875 + 23878547)

deux points voisins auront des "initialiseurs" très différents, ce qui réduira la correlation. bon après, le random de coffee est certainement assez mauvais comme générateur, mais tout dépend de l'utilisation qu'on en fait après..

César Vonc
12/05/2010, 00h04
Et voilà, on peut désormais créer son propre motif pour réaliser une fractale simple personnalisée.

Étant donné qu'en script, on ne peut faire que des boîtes de dialogue de classe GeModalDialog, je n'ai pas pu réaliser de champ dans lequel glisser notre motif personnalisé.
J'ai du faire une sorte de détecteur ; si notre motif est bien une cerce nommée SdC Motif perso, ça marche, sinon, un message d'erreur s'affiche.

Un bouton Créer permet d'ajouter directement un exemple de motif personnalisé. Ce motif a pour seul impératif d'être sur le plan XY, du moins ses extrémités.

http://img208.imageshack.us/img208/1834/subdivicerce.png

Quelques exemples de son utilisation :

http://img187.imageshack.us/img187/527/fractalesdc.png

http://img293.imageshack.us/img293/715/fractalecircu.jpg

http://img297.imageshack.us/img297/3582/nidc.jpg

clemz
12/05/2010, 09h53
ouch le dernier exemple troue le derche :lol: . vraiment bien joué César :prie:

genghiskhan
12/05/2010, 10h16
ouais :shock: et avec un HN par dessus le marché tu mets la config a genoux a tous les coups :D

clemz
29/05/2010, 20h50
Pour ceux qui chercheraient à faire une interface en coffee (je pense notamment à Clemz), j'ai trouvé un excellent site japonais qui explique tout (on comprend grâce aux images) :
http://villager-and-c4d.cocolog-nifty.com/blog/2009/08/c4d--02-7a26.html


hey merci Cesar :) !
je zieute ça de suite.

clem