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

Last change on this file since 935 was 935, checked in by Maciej Komosinski, 4 years ago

Utility functions that provide a set of all neuron classes fulfilling given criteria now also filter neuron classes by the desired Model shape type (BALL_AND_STICK or SOLIDS)

  • 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-2020  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, public ModelEnum
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        /// used in internalCheck()
96        enum CheckType {
97                EDITING_CHECK, ///< Used in Model::validate(). Default validation - does not modify elements of the Model.
98                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.
99                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.
100                CHECKPOINT_CHECK     ///< used when storing checkpoint models. Like LIVE_CHECK, excluding consistency check (disjoint parts are acceptable)
101        };
102protected:
103        ShapeType shape;
104
105        SString nameForErrors() const;
106        int internalcheck(CheckType check);
107
108        void init(const Geno &srcgen, bool _using_checkpoints, bool _is_checkpoint);
109        void init();
110
111        void delMap();
112        void delF0Map();
113        void initMap();
114        void initF0Map();
115
116public:
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);
135        ShapeType getShapeType() const { return shape; }
136        bool isUsingCheckpoints() const { return using_checkpoints; }
137        bool isCheckpoint() const { return is_checkpoint; }
138
139        void updateRefno(); // set ::refno for all elements
140
141        int getCheckpointCount();
142        Model *getCheckpoint(int i);
143
144        /// The bounding box size. Valid if the model is valid. Read only.
145        Pt3D size;
146
147        SString vis_style;
148        double startenergy;
149        Callback delmodel_list;
150        ModelUserTags userdata;
151
152        /// Create empty model with invalid empty genotype
153        Model();
154
155        /** Create a model based on provided genotype
156           @param buildmaps if not 0, generate mapping information for the model.
157           default is 0, because mapping uses additional time and memory.
158           @see getMap()
159           */
160        Model(const Geno &src, bool buildmaps = false, bool _using_checkpoints = false, bool _is_checkpoint = false);
161        Model(const Model &mod, bool buildmaps = false, bool _using_checkpoints = false, bool _is_checkpoint = false);
162        /** duplicate the model.
163                the resulting object's status is 'building' (opened).
164                @see getStatus()
165                */
166        void operator=(const Model &source);
167
168        /** move all elements from 'source' into our model object.
169                'source' becomes empty after this operation.
170                the model will be opened if it is not already open.
171                @see addElementsFrom(const Model &source);
172                */
173        void moveElementsFrom(Model &source);
174
175        /** copy all elements from 'source' into our model object
176                without affecting the 'source'.
177                the model will be opened if it is not already open.
178                @see moveElementsFrom(Model &source);
179                */
180        void addElementsFrom(const Model &source)
181        {
182                Model m(source); moveElementsFrom(m);
183        }
184
185        void operator+=(const Model &source)
186        {
187                addElementsFrom(source);
188        }
189
190        ~Model();
191
192        /** @return source genotype.
193                @warn source genotype will not automatically change
194                when the model is modified. this behaviour is inconsistent
195                with the previous release. use getF0Geno() if you need
196                the updated genotype.
197                @see getF0Geno(), setGeno()
198                */
199        const Geno &getGeno() const;
200
201        /// change source genotype
202        void setGeno(const Geno &newgeno);
203
204        /** @return f0 genotype - generated from current model state
205                don't use between open()-close()
206                */
207        const Geno getF0Geno();
208
209        /// make f0 genotype from current construction (low level version of getF0Geno)
210        void makeGeno(Geno &, MultiMap *map = 0, bool handle_defaults = true);
211
212        /** @return Mapping from source genotype (0-based position in text) to model elements reference numbers.
213                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
214                The map can be empty if the mapping hasn't been requested earlier (in constructor)
215                or the converters don't support mapping.
216                If you create or modify the model using singleStepBuild() or direct manipulation
217                the map will be not changed or created automatically - it is your responsibility.
218                @see Model(const Geno &src,int buildmaps=0), singleStepBuild(), PartBase::addMapping()
219                @see clearMap()
220                @see convmap
221
222                */
223        MultiMap &getMap();
224
225        /** Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
226                @return mapping from f0 genotype (0-based position in text) to model elements reference numbers
227                */
228        const MultiMap &getF0Map();
229
230        /** discard all mapping information for this model.
231                getMap().clear() also works, but it doesn't remove mappings from model elements.
232                If there are any mappings, they will be incorporated into model map during close().
233                @see close(), getMap(), PartBase::clearMapping()
234                */
235        void clearMap();
236
237        /** Generate mapping from the current genotype to the f0 genotype.
238                This works only if both current and f0 maps are already known.
239                Read about how mappings work: http://www.framsticks.com/files/common/GeneticMappingsInArtificialGenomes.pdf
240                @see convmap
241                */
242        void getCurrentToF0Map(MultiMap &m);
243
244        void setValidationLevel(int level)
245        {
246                checklevel = level;
247        }
248
249        /// calculate location of the new part connected to the existing one
250        /// using delta option
251        Pt3D whereDelta(const Part &start, const Pt3D &rot, const Pt3D &delta);
252
253        /// create the whole model from scratch, using current genotype
254        void rebuild(bool buildmaps);
255
256        /// setGeno(newgeno); rebuild();
257        void rebuild(const Geno &newgeno, bool buildmaps) { setGeno(newgeno); rebuild(buildmaps); }
258
259        /// reuse current model object but discard all model data
260        void clear();
261
262        enum ItemType { UnknownType, ModelType, PartType, JointType, NeuronType, NeuronConnectionType, CheckpointType };
263        static ItemType itemTypeFromLinePrefix(const char *line);
264        /** Execute single line of <B>f0</B> genotype.
265                Return value is non-negative reference number of the created item,
266                or negative value. reference number can be used to access
267                the item using getPart(int), getJoint(int) and getNeuroItem(int) methods.
268                @param line_num optional line number used in error messages
269                @param srcrange source genotype range which will be mapped to this element
270                */
271        int addFromString(ItemType item_type, const SString &singleline, int line_num, const MultiRange *srcrange = NULL);
272        /** Execute single line of <B>f0</B> genotype - compatiblity variant */
273        int addFromString(ItemType item_type, const SString &singleline, const MultiRange *srcrange = NULL);
274        /** Execute single line of <B>f0</B> genotype - low level variant, used by Model::build(), error messages returned as string instead of calling logger */
275        int addFromStringNoLog(ItemType item_type, const SString &singleline, SString &error_message, const MultiRange *srcrange = 0);
276
277        /// separate build stages (for future use)
278        void checkpoint();
279
280        /// call resetDelta() on all joints
281        void resetAllDelta();
282
283        /// call useDelta() on all joints
284        void useAllDelta(bool yesno);
285
286        /// Final validity check of the model, all model data has to be available at this point.
287        /// If the model was modified, the genotype will be also updated.
288        /// It also calls "validate" with all side effects.
289        /// @return > 0 means "valid"
290        int close(bool building_live_model = false);
291
292        /// Enable model building.
293        /// You should use it if you need to create new model, modify the model after close
294        /// or modify the model created from the genotype.
295        /// Between open() and close() the model is not fully usable.
296        void open(bool _using_checkpoints = false, bool _is_checkpoint = false);
297
298        /// Current model written as f0 genotype while building
299        /// (not cached, not validated, probably unusable and bad if used before close(). But good for debugging.)
300        Geno rawGeno();
301
302        /// partial validity check - you can use this call
303        /// anytime between open - close.
304        /// this function will check (and repair)
305        /// - part-joint-neuro connections
306        /// - model geometry (if "delta option" was used)
307        /// - physical/biological limits
308        /// @return 1 = valid
309        /// @return 0 = invalid
310        /// validate doesn't make the model fully usable (for simulation)
311        /// you still need to use close if you have changed anything
312        int validate();
313
314        int getPartCount() const;
315        /// you can access parts 0 .. getPartCount()-1.
316        Part *getPart(int i) const;
317
318        int getJointCount() const;
319        /// you can access joints 0 .. getJointCount()-1.
320        Joint *getJoint(int i) const;
321
322        int getNeuroCount() const;
323        int getConnectionCount() const;
324        /// you can access neurons 0 .. getNeuroCount()-1.
325        Neuro *getNeuro(int i) const;
326
327        /** create new Part and add it to the model. @see addPart()  */
328        Part *addNewPart(Part::Shape shape = Part::SHAPE_BALL_AND_STICK) { return addPart(new Part(shape)); }
329        /** create new Joint and add it to the model. @see addJoint() */
330        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; }
331        /** create new Neuro and add it to the model. @see addNeuro() */
332        Neuro *addNewNeuro() { return addNeuro(new Neuro()); }
333
334        /** add p to the model. p->refno is adjusted. @return the Part just added (==p). */
335        Part *addPart(Part *p);
336        /** add j to the model. j->refno is adjusted. @return the Joint just added (==j). */
337        Joint *addJoint(Joint *j);
338        /** add n to the model. n->refno is adjusted. @return the Neuro just added (==n). */
339        Neuro *addNeuro(Neuro *n);
340
341        /** remove the part from model.
342                @param removeattachedjoints if not 0 -> remove all joints connected with this part
343                @param removeattachedneurons if not 0 -> remove neurons attached to this part */
344        void removePart(int partindex, int removeattachedjoints = 1, int removeattachedneurons = 1);
345
346        /** remove the joint from model.
347                @param removeattachedneurons if not 0 -> remove neurons attached to this joint */
348        void removeJoint(int jointindex, int removeattachedneurons = 1);
349
350        /** remove the neuron from model.
351                @param removereferences if true -> look for references to this neuron
352                (i.e. connections from other neurons) and remove them as well */
353        void removeNeuro(int neuroindex, bool removereferences = true);
354
355        void removeNeuros(SList &nlist);
356
357        /// @return part index or -1 if not found in the model
358        int findPart(Part *p);
359        /// @return joint index or -1 if not found in the model
360        int findJoint(Joint *j);
361        /// @return neuro index or -1 if not found in the model
362        int findNeuro(Neuro *nu);
363        /// @return joint index or -1 if not found in the model
364        int findJoint(Part *p1, Part *p2);
365
366        /** make the list of neuros satisfying given search criteria: classname,part,joint
367                @param result objects will be appended here
368                @return number of objects found  */
369        int findNeuros(SList &result, const char *classname = 0, const Part *part = 0, const Joint *joint = 0);
370
371        /** search for joints connected to the part
372                @param result objects will be appended here
373                @return number of objects found  */
374        int findJoints(SList &result, const Part *part = 0);
375
376        void disturb(double amount);
377        void move(const Pt3D &shift);
378        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
379        void rotate(const Orient &rotation);
380        /// rotate around the origin (move-rotate-move to rotate around arbitrary point)
381        void rotate(const Pt3D &angles) { Orient o = Orient_1; o.rotate(angles); rotate(o); }
382
383        /// build this model using solid shape types, based on the provided ball-and-stick model. See also shapeconvert.cpp.
384        void buildUsingSolidShapeTypes(const Model &src_ballandstick_shapes, Part::Shape use_shape = Part::SHAPE_CYLINDER, double thickness = 0.2);
385
386protected:
387        static const int MODEL_MAPPING_OFFSET = 0x10000000;
388public:
389        static int elementToMap(ItemType t, int i);
390        struct TypeAndIndex
391        {
392                ItemType type; int index;
393                TypeAndIndex() :type(UnknownType), index(0) {}
394                TypeAndIndex(ItemType _type, int _index) :type(_type), index(_index) {}
395        };
396        static TypeAndIndex mapToElement(int i);
397        static int partToMap(int i);
398        static int jointToMap(int i);
399        static int neuroToMap(int i);
400        static int mapToPart(int i);
401        static int mapToJoint(int i);
402        static int mapToNeuro(int i);
403
404        static void makeGenToGenMap(MultiMap &result, const MultiMap &gen1tomodel, const MultiMap &gen2tomodel);
405
406        ///////////////////////////
407
408        static Part_MinMaxDef &getMinPart();
409        static Part_MinMaxDef &getMaxPart();
410        static Part_MinMaxDef &getDefPart();
411        static Joint &getMinJoint();
412        static Joint &getMaxJoint();
413        static Joint &getDefJoint();
414        static Neuro &getMinNeuro();
415        static Neuro &getMaxNeuro();
416        static Neuro &getDefNeuro();
417};
418
419/**
420   An object of this class is created from a Model and returns the solids-type const Model& regardless of the source Model shape type.
421   For solids-type Models, the same source Model reference is returned.
422   For ball-and-stick-type Models, the new temporary Model is created (using Model::buildUsingSolidShapeTypes).
423   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:
424
425   void fun(const Model& input) // 'input' can be any shape type
426   {
427   SolidsShapeTypeModel converted(input); // 'converted' contains either 'input' or the new solids-type Model created from 'input'
428   functionAcceptingSolidsTypeModel(converted); // operator const Model&() is called automatically because of the function signature
429   int n=converted.getModel().getPartCount(); // getting the const Model& explicitly (converted.getPartCount() would fail)
430   }
431   */
432class SolidsShapeTypeModel
433{
434public:
435        Model *converted_model;
436        Model *using_model;
437        SolidsShapeTypeModel(Model &m, Part::Shape use_shape = Part::SHAPE_CYLINDER, double thickness = 0.2);
438        operator Model &() { return *using_model; }
439        Model &getModel() { return *using_model; }
440        ~SolidsShapeTypeModel() { if (converted_model) delete converted_model; }
441};
442
443#endif
Note: See TracBrowser for help on using the repository browser.