source: cpp/frams/model/model.h @ 726

Last change on this file since 726 was 726, checked in by Maciej Komosinski, 6 years ago
  • Changed Model::singleStepBuild() to Model::addFromString() to create model elements; the latter requires explicit indication of element type (P/J/N/C)
  • Removed old compatibility source (#ifdef MODEL_V1_COMPATIBLE) from f1->f0 converter and neuron definitions
  • Property svn:eol-style set to native
File size: 15.7 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
[109]4
5#ifndef _MODEL_H_
6#define _MODEL_H_
7
8#include <common/nonstd_math.h>
9#include <stdlib.h>
10#include <stdio.h>
11
12#include "modelparts.h"
13#include <frams/util/advlist.h>
14#include <frams/util/usertags.h>
15
16extern ParamEntry f0_model_paramtab[];
17
18//#define EASYMAPDEBUG
19
[522]20enum ModelBuildStatus { empty, building, invalid, valid };
[109]21
22class MultiMap;
23
24class VisualModel;
25
26/**
[522]27        "Model" is the skeleton of the Framsticks creature.
28        This object can be used for 2 purposes:
29        - you can build a creature from any supported Framsticks genotype
30        format
31        - or generate low level f0 genotype from existing construct.
[109]32
[522]33        In both cases you have access to geometry and neuron net data.
34        Using this standard class assures compatibility and good
35        integration with core Framsticks engine.
[109]36
[522]37        Model contains 3 kinds of objects:
38        - Parts (class Part).
39        - Joints (class Joint). Each Joint is connected with 2 Parts. (@see Joint::attachToParts()).
40        - Neurons (class Neuro). Neuron can have 0 or more inputs - other neurons. (@see Neuro::addInput()).
41        Each Neuron can be located on the physical structure, i.e. it can ba attached to Part or Joint
42        (@see Neuro::attachToPart(), Neuro::attachToJoint()).
[109]43
[522]44        \f[(dot)
45        digraph Model
46        {
47        Joint1; Joint2;
48        node [ shape=box ]
49        Part1; Part2; Part3;
50        Joint1 -> Part1; Joint1 -> Part2; Joint2 -> Part2; Joint2 -> Part3
51        node [ shape=diamond ]
52        Neuro1 -> Neuro2; Neuro1 -> Neuro3; Neuro2 -> Neuro2; Neuro3 -> Neuro2;
53        Neuro1 -> Part1; Neuro2 -> Joint2;
54        }
55        \f]
56        */
[109]57
[522]58class Model : public DestrBase
[109]59{
[522]60protected:
61        Geno geno, f0geno;
62        char modelfromgenotype;
63        char f0genoknown;
64        /// make model map in build()
65        bool autobuildmaps;
66        /// valid if build from f0 genotype
67        int f0errorposition;
68        /// valid if build from f0 genotype
69        int f0warnposition;
[109]70
[522]71        ModelBuildStatus buildstatus;
72        /// NULL if the map is not (yet) created
73        MultiMap *map, *f0map;
[109]74
[522]75        SList parts, joints, neurons;
76        char partmappingchanged;
[109]77
[522]78        void internalCopy(const Model &mod);
[109]79
[522]80        /// make the model from current genotype
81        void build();
[109]82
[522]83        friend class NeuroNetFactory;
84        friend class VisualModel;
85        friend class GLVisualModel;
86        friend class Creature;
87        friend class PartBase;
[109]88
[522]89        int checklevel;
[109]90
[522]91public:
[544]92        enum ShapeType { SHAPE_UNKNOWN, SHAPE_ILLEGAL, SHAPE_BALL_AND_STICK, SHAPE_SOLIDS };
[610]93        /// used in internalCheck()
94        enum CheckType {
[611]95                EDITING_CHECK, ///< Used in Model::validate(). Default validation - does not modify elements of the Model.
96                FINAL_CHECK,   ///< Used in Model::close() when a Model is built from a genotype. Like EDITING_CHECK, but also calculates Joint::d and Joint::rot.
97                LIVE_CHECK     ///< used in Model::close() when a Model is built from a Creature. Like FINAL_CHECK but does not limit joint length which could make some liveModels invalid.
[610]98        };
[522]99protected:
[544]100        ShapeType shape;
[269]101
[522]102        void updateNeuroRefno(); // set Neuro::refno for all neurons
103        SString nameForErrors() const;
[610]104        int internalcheck(CheckType check);
[109]105
[522]106        void moveNeuro(int oldpos, int newpos);
[109]107
[522]108        void init(const Geno &srcgen);
109        void init();
[109]110
[522]111        void delMap();
112        void delF0Map();
113        void initMap();
114        void initF0Map();
[109]115
116public:
[522]117        /** get current model state.
118        \f[(dot)
119        digraph M
120        {
121        node [fontsize=12]
122        edge [fontsize=10]
123        building [label="building = can be modified"]
124        valid -> building [label="open()"]
125        building -> valid [label="close()"]
126        invalid -> building [label="open()"]
127        building -> invalid [label="close() [failed]"]
128        empty -> building [label="open()"]
129        }
130        \f]
131        */
132        ModelBuildStatus getStatus() const { return buildstatus; }
133        int isValid() const { return buildstatus == valid; }
134        int getErrorPosition(bool includingwarnings = false);
[544]135        ShapeType getShapeType() const { return shape; }
[109]136
[522]137        void updateRefno(); // set ::refno for all elements
[109]138
[522]139        /// The bounding box size. Valid if the model is valid. Read only.
140        Pt3D size;
[109]141
[522]142        SString vis_style;
143        double startenergy;
144        Callback delmodel_list;
145        ModelUserTags userdata;
[109]146
[522]147        /// Create empty model with invalid empty genotype
148        Model();
[109]149
[522]150        /** Create a model based on provided genotype
151           @param buildmaps if not 0, generate mapping information for the model.
152           default is 0, because mapping uses additional time and memory.
153           @see getMap()
154           */
155        Model(const Geno &src, bool buildmaps = false);
156        Model(const Model &mod, bool buildmaps = false);
157        /** duplicate the model.
158                the resulting object's status is 'building' (opened).
159                @see getStatus()
160                */
161        void operator=(const Model &source);
[109]162
[522]163        /** move all elements from 'source' into our model object.
164                'source' becomes empty after this operation.
165                the model will be opened if it is not already open.
166                @see addElementsFrom(const Model &source);
167                */
168        void moveElementsFrom(Model &source);
[109]169
[522]170        /** copy all elements from 'source' into our model object
171                without affecting the 'source'.
172                the model will be opened if it is not already open.
173                @see moveElementsFrom(Model &source);
174                */
175        void addElementsFrom(const Model &source)
176        {
177                Model m(source); moveElementsFrom(m);
178        }
[109]179
[522]180        void operator+=(const Model &source)
181        {
182                addElementsFrom(source);
183        }
[109]184
[522]185        ~Model();
[109]186
[522]187        /** @return source genotype.
188                @warn source genotype will not automatically change
189                when the model is modified. this behaviour is inconsistent
190                with the previous release. use getF0Geno() if you need
191                the updated genotype.
192                @see getF0Geno(), setGeno()
193                */
194        const Geno &getGeno() const;
[109]195
[522]196        /// change source genotype
197        void setGeno(const Geno& newgeno);
[109]198
[522]199        /** @return f0 genotype - generated from current model state
200                don't use between open()-close()
201                */
202        const Geno getF0Geno();
[109]203
[522]204        /// make f0 genotype from current construction (low level version of getF0Geno)
205        void makeGeno(Geno &, MultiMap *map = 0, bool handle_defaults = true);
[109]206
[522]207        /** @return Mapping from source genotype (0-based position in text) to model elements reference numbers.
208                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
209                The map can be empty if the mapping hasn't been requested earlier (in constructor)
210                or the converters don't support mapping.
211                If you create or modify the model using singleStepBuild() or direct manipulation
212                the map will be not changed or created automatically - it is your responsibility.
213                @see Model(const Geno &src,int buildmaps=0), singleStepBuild(), PartBase::addMapping()
214                @see clearMap()
215                @see convmap
[109]216
[522]217                */
218        MultiMap &getMap();
[109]219
[522]220        /** Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
221                @return mapping from f0 genotype (0-based position in text) to model elements reference numbers
222                */
223        const MultiMap &getF0Map();
[109]224
[522]225        /** discard all mapping information for this model.
226                getMap().clear() also works, but it doesn't remove mappings from model elements.
227                If there are any mappings, they will be incorporated into model map during close().
228                @see close(), getMap(), PartBase::clearMapping()
229                */
230        void clearMap();
[109]231
[522]232        /** Generate mapping from the current genotype to the f0 genotype.
233                This works only if both current and f0 maps are already known.
234                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
235                @see convmap
236                */
237        void getCurrentToF0Map(MultiMap& m);
[109]238
[522]239        void setValidationLevel(int level)
240        {
241                checklevel = level;
242        }
[109]243
[522]244        /// calculate location of the new part connected to the existing one
245        /// using delta option
246        Pt3D whereDelta(const Part& start, const Pt3D& rot, const Pt3D& delta);
[109]247
[522]248        /// create the whole model from scratch, using current genotype
249        void rebuild(bool buildmaps);
[109]250
[522]251        /// setGeno(newgeno); rebuild();
252        void rebuild(const Geno& newgeno, bool buildmaps) { setGeno(newgeno); rebuild(buildmaps); }
[109]253
[522]254        /// reuse current model object but discard all model data
255        void clear();
[109]256
[726]257        enum ItemType { UnknownType,ModelType,PartType,JointType,NeuronType,NeuronConnectionType };
258        static ItemType itemTypeFromLinePrefix(const char* line);
[522]259        /** Execute single line of <B>f0</B> genotype.
260                Return value is non-negative reference number of the created item,
261                or negative value. reference number can be used to access
262                the item using getPart(int), getJoint(int) and getNeuroItem(int) methods.
263                @param line_num optional line number used in error messages
264                @param srcrange source genotype range which will be mapped to this element
265                */
[726]266        int addFromString(ItemType item_type,const SString &singleline, int line_num, const MultiRange* srcrange = NULL);
[522]267        /** Execute single line of <B>f0</B> genotype - compatiblity variant */
[726]268        int addFromString(ItemType item_type,const SString &singleline, const MultiRange* srcrange = NULL);
[522]269        /** Execute single line of <B>f0</B> genotype - low level variant, used by Model::build(), error messages returned as string instead of calling logger */
[726]270        int addFromStringNoLog(ItemType item_type,const SString &singleline, SString& error_message, const MultiRange* srcrange = 0);
[109]271
[522]272        /// separate build stages (for future use)
273        void checkpoint();
[109]274
[522]275        /// call resetDelta() on all joints
276        void resetAllDelta();
[109]277
[522]278        /// call useDelta() on all joints
279        void useAllDelta(bool yesno);
[109]280
[522]281        /// Final validity check of the model, all model data has to be available at this point.
282        /// If the model was modified, the genotype will be also updated.
283        /// It also calls "validate" with all side effects.
284        /// @return > 0 means "valid"
[611]285        int close(bool building_live_model = false);
[109]286
[522]287        /// Enable model building.
288        /// You should use it if you need to create new model, modify the model after close
289        /// or modify the model created from the genotype.
290        /// Between open() and close() the model is not fully usable.
291        void open();
[109]292
[522]293        /// Current model written as f0 genotype while building
294        /// (not cached, not validated, probably unusable and bad if used before close(). But good for debugging.)
295        Geno rawGeno();
[109]296
[522]297        /// partial validity check - you can use this call
298        /// anytime between open - close.
299        /// this function will check (and repair)
300        /// - part-joint-neuro connections
301        /// - model geometry (if "delta option" was used)
302        /// - physical/biological limits
303        /// @return 1 = valid
304        /// @return 0 = invalid
305        /// validate doesn't make the model fully usable (for simulation)
306        /// you still need to use close if you have changed anything
307        int validate();
[109]308
[522]309        int getPartCount() const;
310        /// you can access parts 0 .. getPartCount()-1.
311        Part *getPart(int i) const;
[109]312
[522]313        int getJointCount() const;
314        /// you can access joints 0 .. getJointCount()-1.
315        Joint *getJoint(int i) const;
[109]316
[522]317        int getNeuroCount() const;
318        int getConnectionCount() const;
319        /// you can access neurons 0 .. getNeuroCount()-1.
320        Neuro *getNeuro(int i) const;
[109]321
[522]322        /** create new Part and add it to the model. @see addPart()  */
[544]323        Part *addNewPart(Part::Shape shape = Part::SHAPE_BALL_AND_STICK) { return addPart(new Part(shape)); }
[522]324        /** create new Joint and add it to the model. @see addJoint() */
[544]325        Joint *addNewJoint(Part *p1 = NULL, Part *p2 = NULL, Joint::Shape shape = Joint::SHAPE_BALL_AND_STICK) { Joint *j = addJoint(new Joint()); j->shape = shape; if ((p1 != NULL) && (p2 != NULL)) j->attachToParts(p1, p2); return j; }
[522]326        /** create new Neuro and add it to the model. @see addNeuro() */
327        Neuro *addNewNeuro() { return addNeuro(new Neuro()); }
[109]328
[522]329        /** add p to the model. p->refno is adjusted. @return the Part just added (==p). */
330        Part *addPart(Part *p);
331        /** add j to the model. j->refno is adjusted. @return the Joint just added (==j). */
332        Joint *addJoint(Joint *j);
333        /** add n to the model. n->refno is adjusted. @return the Neuro just added (==n). */
334        Neuro *addNeuro(Neuro *n);
[109]335
[522]336        /** remove the part from model.
337                @param removeattachedjoints if not 0 -> remove all joints connected with this part
338                @param removeattachedneurons if not 0 -> remove neurons attached to this part */
339        void removePart(int partindex, int removeattachedjoints = 1, int removeattachedneurons = 1);
[109]340
[522]341        /** remove the joint from model.
342                @param removeattachedneurons if not 0 -> remove neurons attached to this joint */
343        void removeJoint(int jointindex, int removeattachedneurons = 1);
[109]344
[522]345        /** remove the neuron from model.
346                @param removereferences if true -> look for references to this neuron
347                (i.e. connections from other neurons) and remove them as well */
348        void removeNeuro(int neuroindex, bool removereferences = true);
[109]349
[522]350        void removeNeuros(SList &nlist);
[109]351
[522]352        /// @return part index or -1 if not found in the model
353        int findPart(Part* p);
354        /// @return joint index or -1 if not found in the model
355        int findJoint(Joint* j);
356        /// @return neuro index or -1 if not found in the model
357        int findNeuro(Neuro* nu);
358        /// @return joint index or -1 if not found in the model
359        int findJoint(Part *p1, Part *p2);
[109]360
[522]361        /** make the list of neuros satisfying given search criteria: classname,part,joint
362                @param result objects will be appended here
363                @return number of objects found  */
364        int findNeuros(SList& result, const char* classname = 0, const Part* part = 0, const Joint* joint = 0);
[109]365
[522]366        /** search for joints connected to the part
367                @param result objects will be appended here
368                @return number of objects found  */
369        int findJoints(SList& result, const Part* part = 0);
[109]370
[522]371        void disturb(double amount);
372        void move(const Pt3D& shift);
373        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
374        void rotate(const Orient& rotation);
375        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
376        void rotate(const Pt3D& angles) { Orient o = Orient_1; o.rotate(angles); rotate(o); }
[109]377
[546]378        /// build this model using solid shape types, based on the provided ball-and-stick model. See also shapeconvert.cpp.
379        void buildUsingSolidShapeTypes(const Model& src_ballandstick_shapes, Part::Shape use_shape = Part::SHAPE_CYLINDER, float thickness = 0.2);
[269]380
[109]381#ifdef EASYMAPDEBUG
[522]382        static int partToMap(int i) {return 0+i;}
383        static int jointToMap(int i) {return 10+i;}
384        static int neuroToMap(int i) {return 20+i;}
385        static int mapToPart(int i) {return i-0;}
386        static int mapToJoint(int i) {return i-10;}
387        static int mapToNeuro(int i) {return i-20;}
[109]388#else
[522]389        static int partToMap(int i) { return 0x10000000 + i; }
390        static int jointToMap(int i) { return 0x20000000 + i; }
391        static int neuroToMap(int i) { return 0x30000000 + i; }
392        static int mapToPart(int i) { return i - 0x10000000; }
393        static int mapToJoint(int i) { return i - 0x20000000; }
394        static int mapToNeuro(int i) { return i - 0x30000000; }
[109]395#endif
396
[522]397        static void makeGenToGenMap(MultiMap& result, const MultiMap& gen1tomodel, const MultiMap& gen2tomodel);
[109]398
[522]399        ///////////////////////////
[109]400
[522]401        static Part& getMinPart();
402        static Part& getMaxPart();
403        static Part& getDefPart();
404        static Joint& getMinJoint();
405        static Joint& getMaxJoint();
406        static Joint& getDefJoint();
407        static Neuro& getMinNeuro();
408        static Neuro& getMaxNeuro();
409        static Neuro& getDefNeuro();
[109]410};
411
[546]412/**
413   An object of this class is created from a Model and returns the solids-type const Model& regardless of the source Model shape type.
414   For solids-type Models, the same source Model reference is returned.
415   For ball-and-stick-type Models, the new temporary Model is created (using Model::buildUsingSolidShapeTypes).
416   Useful for making the solids-only code work for both solids Models and ball-and-stick Models, without if's and special cases, like in this example:
[611]417
[546]418   void fun(const Model& input) // 'input' can be any shape type
419   {
[611]420   SolidsShapeTypeModel converted(input); // 'converted' contains either 'input' or the new solids-type Model created from 'input'
421   functionAcceptingSolidsTypeModel(converted); // operator const Model&() is called automatically because of the function signature
422   int n=converted.getModel().getPartCount(); // getting the const Model& explicitly (converted.getPartCount() would fail)
[546]423   }
[611]424   */
[546]425class SolidsShapeTypeModel
426{
427public:
428        Model *converted_model;
[660]429        Model *using_model;
430        SolidsShapeTypeModel(Model& m, Part::Shape use_shape = Part::SHAPE_CYLINDER, float thickness = 0.2);
431        operator Model&() { return *using_model; }
432        Model& getModel() { return *using_model; }
[546]433        ~SolidsShapeTypeModel() { if (converted_model) delete converted_model; }
434};
435
[109]436#endif
Note: See TracBrowser for help on using the repository browser.