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

Last change on this file since 610 was 610, checked in by Maciej Komosinski, 4 years ago
  • Introduced levels of checking the validity of a Model
  • If a Joint is too long, print how much it exceeds the maximum allowed length
  • Property svn:eol-style set to native
File size: 17.0 KB
Line 
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.
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
20enum ModelBuildStatus { empty, building, invalid, valid };
21
22class MultiMap;
23
24class VisualModel;
25
26/**
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.
32
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.
36
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()).
43
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        */
57
58class Model : public DestrBase
59{
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;
70
71        ModelBuildStatus buildstatus;
72        /// NULL if the map is not (yet) created
73        MultiMap *map, *f0map;
74
75        SList parts, joints, neurons;
76        char partmappingchanged;
77
78#ifdef MODEL_V1_COMPATIBLE
79        /** NeuroCount value.
80                compatibility_neurocount = -1 if its value is unknown and the layout is not compatible
81                @see reorderToOldLayout()
82                */
83        int oldneurocount;
84        char oldconnections;
85
86        /** calculate oldNeuroCount */
87        void calcOldNeuroCount();
88        /** some new calls can invalidate old compatibility data */
89        void invalidateOldNeuroCount() {oldneurocount=-1;}
90        /**
91           for compatibility with old NeuroClass the layout of Neurons
92           is changed when old 'Neuro' accessing methods are in use.
93           Neurons:
94           0 .. compatibility_neurocount-1                : old Neurons (class "N")
95           compatibility_neurocount .. neurons.size()-1   : other units
96           */
97        int reorderToOldLayout();
98
99        /** check if compatibility should be preserved */
100        int hasOldNeuroLayout() {return oldneurocount>=0;}
101
102        /** add inputs to the old "-" units.
103                @return 1=ok, 0=invalid input detected  */
104        int addOldConnectionsInputs();
105#endif
106
107        void internalCopy(const Model &mod);
108
109        /// make the model from current genotype
110        void build();
111
112        friend class NeuroNetFactory;
113        friend class VisualModel;
114        friend class GLVisualModel;
115        friend class Creature;
116        friend class PartBase;
117
118        int checklevel;
119
120public:
121        enum ShapeType { SHAPE_UNKNOWN, SHAPE_ILLEGAL, SHAPE_BALL_AND_STICK, SHAPE_SOLIDS };
122        /// used in internalCheck()
123        enum CheckType {
124         EDITING_CHECK, ///< Used in Model::validate(). Default validation - does not modify elements of the Model.
125         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.
126         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.
127        };
128protected:
129        ShapeType shape;
130
131        void updateNeuroRefno(); // set Neuro::refno for all neurons
132        SString nameForErrors() const;
133        int internalcheck(CheckType check);
134
135        void moveNeuro(int oldpos, int newpos);
136
137        void init(const Geno &srcgen);
138        void init();
139
140        void delMap();
141        void delF0Map();
142        void initMap();
143        void initF0Map();
144
145public:
146        /** get current model state.
147        \f[(dot)
148        digraph M
149        {
150        node [fontsize=12]
151        edge [fontsize=10]
152        building [label="building = can be modified"]
153        valid -> building [label="open()"]
154        building -> valid [label="close()"]
155        invalid -> building [label="open()"]
156        building -> invalid [label="close() [failed]"]
157        empty -> building [label="open()"]
158        }
159        \f]
160        */
161        ModelBuildStatus getStatus() const { return buildstatus; }
162        int isValid() const { return buildstatus == valid; }
163        int getErrorPosition(bool includingwarnings = false);
164        ShapeType getShapeType() const { return shape; }
165
166        void updateRefno(); // set ::refno for all elements
167
168        /// The bounding box size. Valid if the model is valid. Read only.
169        Pt3D size;
170
171        SString vis_style;
172        double startenergy;
173        Callback delmodel_list;
174        ModelUserTags userdata;
175
176        /// Create empty model with invalid empty genotype
177        Model();
178
179        /** Create a model based on provided genotype
180           @param buildmaps if not 0, generate mapping information for the model.
181           default is 0, because mapping uses additional time and memory.
182           @see getMap()
183           */
184        Model(const Geno &src, bool buildmaps = false);
185        Model(const Model &mod, bool buildmaps = false);
186        /** duplicate the model.
187                the resulting object's status is 'building' (opened).
188                @see getStatus()
189                */
190        void operator=(const Model &source);
191
192        /** move all elements from 'source' into our model object.
193                'source' becomes empty after this operation.
194                the model will be opened if it is not already open.
195                @see addElementsFrom(const Model &source);
196                */
197        void moveElementsFrom(Model &source);
198
199        /** copy all elements from 'source' into our model object
200                without affecting the 'source'.
201                the model will be opened if it is not already open.
202                @see moveElementsFrom(Model &source);
203                */
204        void addElementsFrom(const Model &source)
205        {
206                Model m(source); moveElementsFrom(m);
207        }
208
209        void operator+=(const Model &source)
210        {
211                addElementsFrom(source);
212        }
213
214        ~Model();
215
216        /** @return source genotype.
217                @warn source genotype will not automatically change
218                when the model is modified. this behaviour is inconsistent
219                with the previous release. use getF0Geno() if you need
220                the updated genotype.
221                @see getF0Geno(), setGeno()
222                */
223        const Geno &getGeno() const;
224
225        /// change source genotype
226        void setGeno(const Geno& newgeno);
227
228        /** @return f0 genotype - generated from current model state
229                don't use between open()-close()
230                */
231        const Geno getF0Geno();
232
233        /// make f0 genotype from current construction (low level version of getF0Geno)
234        void makeGeno(Geno &, MultiMap *map = 0, bool handle_defaults = true);
235
236        /** @return Mapping from source genotype (0-based position in text) to model elements reference numbers.
237                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
238                The map can be empty if the mapping hasn't been requested earlier (in constructor)
239                or the converters don't support mapping.
240                If you create or modify the model using singleStepBuild() or direct manipulation
241                the map will be not changed or created automatically - it is your responsibility.
242                @see Model(const Geno &src,int buildmaps=0), singleStepBuild(), PartBase::addMapping()
243                @see clearMap()
244                @see convmap
245
246                */
247        MultiMap &getMap();
248
249        /** Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
250                @return mapping from f0 genotype (0-based position in text) to model elements reference numbers
251                */
252        const MultiMap &getF0Map();
253
254        /** discard all mapping information for this model.
255                getMap().clear() also works, but it doesn't remove mappings from model elements.
256                If there are any mappings, they will be incorporated into model map during close().
257                @see close(), getMap(), PartBase::clearMapping()
258                */
259        void clearMap();
260
261        /** Generate mapping from the current genotype to the f0 genotype.
262                This works only if both current and f0 maps are already known.
263                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
264                @see convmap
265                */
266        void getCurrentToF0Map(MultiMap& m);
267
268        void setValidationLevel(int level)
269        {
270                checklevel = level;
271        }
272
273        /// calculate location of the new part connected to the existing one
274        /// using delta option
275        Pt3D whereDelta(const Part& start, const Pt3D& rot, const Pt3D& delta);
276
277        /// create the whole model from scratch, using current genotype
278        void rebuild(bool buildmaps);
279
280        /// setGeno(newgeno); rebuild();
281        void rebuild(const Geno& newgeno, bool buildmaps) { setGeno(newgeno); rebuild(buildmaps); }
282
283        /// reuse current model object but discard all model data
284        void clear();
285
286        /** Execute single line of <B>f0</B> genotype.
287                Return value is non-negative reference number of the created item,
288                or negative value. reference number can be used to access
289                the item using getPart(int), getJoint(int) and getNeuroItem(int) methods.
290                @param line_num optional line number used in error messages
291                @param srcrange source genotype range which will be mapped to this element
292                */
293        int singleStepBuild(const SString &singleline, int line_num, const MultiRange* srcrange = NULL);
294        /** Execute single line of <B>f0</B> genotype - compatiblity variant */
295        int singleStepBuild(const SString &singleline, const MultiRange* srcrange = NULL);
296        /** Execute single line of <B>f0</B> genotype - low level variant, used by Model::build(), error messages returned as string instead of calling logger */
297        int singleStepBuildNoLog(const SString &singleline, SString& error_message, const MultiRange* srcrange = 0);
298
299        /// separate build stages (for future use)
300        void checkpoint();
301
302        /// call resetDelta() on all joints
303        void resetAllDelta();
304
305        /// call useDelta() on all joints
306        void useAllDelta(bool yesno);
307
308        /// Final validity check of the model, all model data has to be available at this point.
309        /// If the model was modified, the genotype will be also updated.
310        /// It also calls "validate" with all side effects.
311        /// @return > 0 means "valid"
312        int close(bool building_live_model=false);
313
314        /// Enable model building.
315        /// You should use it if you need to create new model, modify the model after close
316        /// or modify the model created from the genotype.
317        /// Between open() and close() the model is not fully usable.
318        void open();
319
320        /// Current model written as f0 genotype while building
321        /// (not cached, not validated, probably unusable and bad if used before close(). But good for debugging.)
322        Geno rawGeno();
323
324        /// partial validity check - you can use this call
325        /// anytime between open - close.
326        /// this function will check (and repair)
327        /// - part-joint-neuro connections
328        /// - model geometry (if "delta option" was used)
329        /// - physical/biological limits
330        /// @return 1 = valid
331        /// @return 0 = invalid
332        /// validate doesn't make the model fully usable (for simulation)
333        /// you still need to use close if you have changed anything
334        int validate();
335
336        int getPartCount() const;
337        /// you can access parts 0 .. getPartCount()-1.
338        Part *getPart(int i) const;
339
340        int getJointCount() const;
341        /// you can access joints 0 .. getJointCount()-1.
342        Joint *getJoint(int i) const;
343
344        int getNeuroCount() const;
345        int getConnectionCount() const;
346        /// you can access neurons 0 .. getNeuroCount()-1.
347        Neuro *getNeuro(int i) const;
348
349#ifdef MODEL_V1_COMPATIBLE
350        /* compatibility calls for accessing old Neuro objects */
351
352        /// @deprecated Neuro class will be removed soon
353        /// @see compat
354        int old_getNeuroCount();
355        /// you can access neurons 0 .. getNeuroCount()-1.
356        /// @deprecated Neuro class will be removed soon
357        Neuro *old_getNeuro(int i);
358        /// @see addNewNeuro, addNeuro
359        /// @deprecated Neuro class will be removed soon
360        Neuro *old_addNewNeuro();
361        /// @return neuro index or -1 if not found
362        /// @deprecated Neuro class will be removed soon
363        int old_findNeuro(Neuro* n);
364#endif
365
366        /** create new Part and add it to the model. @see addPart()  */
367        Part *addNewPart(Part::Shape shape = Part::SHAPE_BALL_AND_STICK) { return addPart(new Part(shape)); }
368        /** create new Joint and add it to the model. @see addJoint() */
369        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; }
370        /** create new Neuro and add it to the model. @see addNeuro() */
371        Neuro *addNewNeuro() { return addNeuro(new Neuro()); }
372
373        /** add p to the model. p->refno is adjusted. @return the Part just added (==p). */
374        Part *addPart(Part *p);
375        /** add j to the model. j->refno is adjusted. @return the Joint just added (==j). */
376        Joint *addJoint(Joint *j);
377        /** add n to the model. n->refno is adjusted. @return the Neuro just added (==n). */
378        Neuro *addNeuro(Neuro *n);
379
380        /** remove the part from model.
381                @param removeattachedjoints if not 0 -> remove all joints connected with this part
382                @param removeattachedneurons if not 0 -> remove neurons attached to this part */
383        void removePart(int partindex, int removeattachedjoints = 1, int removeattachedneurons = 1);
384
385        /** remove the joint from model.
386                @param removeattachedneurons if not 0 -> remove neurons attached to this joint */
387        void removeJoint(int jointindex, int removeattachedneurons = 1);
388
389        /** remove the neuron from model.
390                @param removereferences if true -> look for references to this neuron
391                (i.e. connections from other neurons) and remove them as well */
392        void removeNeuro(int neuroindex, bool removereferences = true);
393
394        void removeNeuros(SList &nlist);
395
396        /// @return part index or -1 if not found in the model
397        int findPart(Part* p);
398        /// @return joint index or -1 if not found in the model
399        int findJoint(Joint* j);
400        /// @return neuro index or -1 if not found in the model
401        int findNeuro(Neuro* nu);
402        /// @return joint index or -1 if not found in the model
403        int findJoint(Part *p1, Part *p2);
404
405        /** make the list of neuros satisfying given search criteria: classname,part,joint
406                @param result objects will be appended here
407                @return number of objects found  */
408        int findNeuros(SList& result, const char* classname = 0, const Part* part = 0, const Joint* joint = 0);
409
410        /** search for joints connected to the part
411                @param result objects will be appended here
412                @return number of objects found  */
413        int findJoints(SList& result, const Part* part = 0);
414
415        void disturb(double amount);
416        void move(const Pt3D& shift);
417        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
418        void rotate(const Orient& rotation);
419        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
420        void rotate(const Pt3D& angles) { Orient o = Orient_1; o.rotate(angles); rotate(o); }
421
422        /// build this model using solid shape types, based on the provided ball-and-stick model. See also shapeconvert.cpp.
423        void buildUsingSolidShapeTypes(const Model& src_ballandstick_shapes, Part::Shape use_shape = Part::SHAPE_CYLINDER, float thickness = 0.2);
424
425#ifdef EASYMAPDEBUG
426        static int partToMap(int i) {return 0+i;}
427        static int jointToMap(int i) {return 10+i;}
428        static int neuroToMap(int i) {return 20+i;}
429        static int mapToPart(int i) {return i-0;}
430        static int mapToJoint(int i) {return i-10;}
431        static int mapToNeuro(int i) {return i-20;}
432#else
433        static int partToMap(int i) { return 0x10000000 + i; }
434        static int jointToMap(int i) { return 0x20000000 + i; }
435        static int neuroToMap(int i) { return 0x30000000 + i; }
436        static int mapToPart(int i) { return i - 0x10000000; }
437        static int mapToJoint(int i) { return i - 0x20000000; }
438        static int mapToNeuro(int i) { return i - 0x30000000; }
439#endif
440
441        static void makeGenToGenMap(MultiMap& result, const MultiMap& gen1tomodel, const MultiMap& gen2tomodel);
442
443        ///////////////////////////
444
445        static Part& getMinPart();
446        static Part& getMaxPart();
447        static Part& getDefPart();
448        static Joint& getMinJoint();
449        static Joint& getMaxJoint();
450        static Joint& getDefJoint();
451        static Neuro& getMinNeuro();
452        static Neuro& getMaxNeuro();
453        static Neuro& getDefNeuro();
454};
455
456/**
457   An object of this class is created from a Model and returns the solids-type const Model& regardless of the source Model shape type.
458   For solids-type Models, the same source Model reference is returned.
459   For ball-and-stick-type Models, the new temporary Model is created (using Model::buildUsingSolidShapeTypes).
460   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:
461   
462   void fun(const Model& input) // 'input' can be any shape type
463   {
464      SolidsShapeTypeModel converted(input); // 'converted' contains either 'input' or the new solids-type Model created from 'input'
465      functionAcceptingSolidsTypeModel(converted); // operator const Model&() is called automatically because of the function signature
466      int n=converted.getModel().getPartCount(); // getting the const Model& explicitly (converted.getPartCount() would fail)
467   }
468 */
469class SolidsShapeTypeModel
470{
471public:
472        Model *converted_model;
473        const Model *using_model;
474        SolidsShapeTypeModel(const Model& m, Part::Shape use_shape = Part::SHAPE_CYLINDER, float thickness = 0.2);
475        operator const Model&() const { return *using_model; }
476        const Model& getModel() const { return *using_model; }
477        ~SolidsShapeTypeModel() { if (converted_model) delete converted_model; }
478};
479
480#endif
Note: See TracBrowser for help on using the repository browser.