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

Last change on this file since 839 was 815, checked in by Maciej Komosinski, 8 months ago

Use double for consistency (using float to store double value of 0.2 would cause imprecision when 0.2f becomes 0.2 in Part shape properties)

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