source: cpp/frams/model/model.h

Last change on this file was 1130, checked in by Maciej Komosinski, 20 months ago

Used std::min(), std::max() explicitly to avoid compiler confusion. Used std::size() explicitly instead of the equivalent macro

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