source: cpp/frams/model/modelparts.h @ 1139

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

Stick thickness when converting "ball-and-stick" models to "solid-shape" models consistent with the traditional visualization of sticks in GUI

  • Property svn:eol-style set to native
File size: 19.3 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 _MODELPARTS_H_
6#define _MODELPARTS_H_
7
8#include <frams/util/3d.h>
9#include <frams/genetics/genoconv.h>
10
11#include <frams/util/extvalue.h>
12#include <frams/util/list.h>
13#include <frams/util/sstring.h>
14#include <frams/util/sstringutils.h>
15#include <frams/param/param.h>
16#include <frams/param/syntparam.h>
17#include <frams/util/usertags.h>
18#include <frams/param/paramtabobj.h>
19
20#include <stdio.h>
21
22class Model;
23class IRange;
24class MultiRange;
25
26typedef UserTags<Model, void *, 5> ModelUserTags;
27
28
29/// Introduced only because we can't have a forward declaration of enum in the Model class,
30/// and we would need a forward declaration because of mutual (cyclic) dependence of declarations of Model and NeuroClass.
31/// https://stackoverflow.com/questions/27019292/is-in-class-enum-forward-declaration-possible
32/// Use Model::... instead of ModelEnum::...
33class ModelEnum
34{
35public:
36        enum ShapeType {
37                SHAPETYPE_BALL_AND_STICK = 0, SHAPETYPE_SOLIDS = 1, SHAPETYPE_UNKNOWN, SHAPETYPE_ILLEGAL, ///< 0 and 1 have special significance - these values allow for bit operations.
38                SHAPETYPE_FIRST = SHAPETYPE_BALL_AND_STICK, SHAPETYPE_LAST = SHAPETYPE_SOLIDS // for iteration
39        };
40};
41
42/** Common base for model elements. */
43class PartBase
44{
45public:
46        SString vis_style;
47        PartBase(const SString &s) :vis_style(s), mapped(0) {}
48        ~PartBase();
49        static SString getDefaultStyle() { return SString("none"); }
50        MultiRange *mapped;
51        enum PartBaseFlags { Selected = 1 };
52        int flags;
53        Model *owner;   ///< backlink to the model
54
55        SString info;
56
57        Model &getModel() const { return *owner; }
58
59        ModelUserTags userdata;
60
61        void notifyMappingChange();
62
63        void clearMapping();
64        MultiRange *getMapping() { return mapped; }
65        void setMapping(const IRange &mr);
66        void addMapping(const IRange &mr);
67        void setMapping(const MultiRange &mr);
68        void addMapping(const MultiRange &mr);
69
70        void setInfo(const SString &name, const SString &value);
71        void setInfo(const SString &name, int value);
72        void setInfo(const SString &name, double value);
73        SString getInfo(const SString &name);
74};
75
76/// Part is the only real physical object in creatures in Framsticks.
77/// You can use this class for querying and adjusting constructed
78/// model properties.
79class Part : public PartBase
80{
81        friend class Model;
82        static SString getDefaultStyle();
83        Part(double _mass, double _size, double _density, double _friction, double _ingest, double _assim)
84                :PartBase(getDefaultStyle()), mass(_mass), size(_size), density(_density), friction(_friction), ingest(_ingest), assim(_assim)
85        {}
86        void defassign();
87public:
88        // base properties - have special meaning and therefore are often accessed directly for convenience
89        Pt3D p;    ///< 3d coordinates of the part
90        Orient o;  ///< orientation in 3d space (rotation matrix)
91        /// ParamInterface object is preferred way to get/set other properties.
92        Param extraProperties();
93        Param properties();
94        paInt refno;
95        Pt3D rot;///< rotation angles
96
97        ///
98        paInt shape;///default=old Framsticks compatible, do not mix with shapes>0
99        enum Shape {
100                SHAPE_BALL = 0, ///<  for "ball and stick" shape type model only.
101                SHAPE_ELLIPSOID = 1, SHAPE_CUBOID = 2, SHAPE_CYLINDER = 3,
102                SHAPE_FIRST = SHAPE_BALL, SHAPE_LAST = SHAPE_CYLINDER // for iteration
103        };
104        static const char* getShapeName(Shape sh);
105        static constexpr double DEFAULT_STICK_RADIUS = 0.2; //used in SHAPETYPE_BALL_AND_STICK to SHAPETYPE_SOLIDS Model conversion
106
107        double mass, size, density, friction, ingest, assim, hollow;
108        Pt3D scale;
109        Pt3D food;
110        //SList points; // collistion points
111        //Slist neurons; // "select * from owner->neurons where part=this" ;-)
112
113        Pt3D vcolor;
114
115        Part(enum Shape s = SHAPE_BALL);
116        Part(const Part &src) :PartBase(getDefaultStyle()) { operator=(src); }
117        void operator=(const Part &src);
118
119        void setPositionAndRotationFromAxis(const Pt3D &p1, const Pt3D &p2);
120        void setOrient(const Orient &o);///< set part.o and calculates part.rot (rotation angles)
121        void setRot(const Pt3D &r);///< set part.rot (rotation angles) and calculate part.o
122
123        static Param &getStaticParam();
124};
125
126class Part_MinMaxDef : public Part //contains additional information for Model::getMinPart()/getMaxPart()/getDefPart()
127{
128public:
129        double volume; ///< Introduced to limit the range of volumes (~mass) in simulation of solids. Genetic operators should observe this min,max volume by calculating and limiting the volume of Parts based on their type and sx,sy,sz.
130
131        void defassign();
132        Part_MinMaxDef() { defassign(); }
133};
134
135/// Imaginary connection between two parts.
136/// Joint has no mass nor intertia but can transfer forces.
137class Joint : public PartBase
138{
139        friend class Model;
140        SString getDefaultStyle();
141        Joint(double _stamina, double _stif, double _rotstif, double _d)
142                :PartBase(getDefaultStyle()), stamina(_stamina), stif(_stif), rotstif(_rotstif)
143        {
144                d = Pt3D(_d, 0, 0);
145        }
146        void defassign();
147        void resetDeltaMarkers();
148public:
149        // base properties:
150        paInt p1_refno, p2_refno; ///< parts' reference numbers
151
152        Part *part1, *part2;    ///< references to parts
153        class Pt3D d;           ///< position delta between parts
154        class Pt3D rot; ///< orientation delta between parts expressed as 3 angles
155        enum Shape {
156                SHAPE_STICK = 0, ///<  for "ball and stick" shape type model, creates a physical (cylinder or cuboid) rod between parts. Do not mix with part.shape>0.
157                SHAPE_FIXED = 1, ///< merge parts into one physical entity
158                SHAPE_HINGE_X = 2, ///< hinge connection, revolving around X axis defined by hinge_pos and hinge_rot
159                SHAPE_HINGE_XY = 3, ///< double hinge connection, revolving around X and Y axes defined by hinge_pos and hinge_rot
160                SHAPE_FIRST = SHAPE_STICK, SHAPE_LAST = SHAPE_HINGE_XY // for iteration
161        };
162        paInt shape;///< values of type Shape (paInt for integration with Param)
163        static const char* getShapeName(Shape sh);
164        class Pt3D hinge_pos; ///< hinge position (relative to part1) for HINGE_X and HINGE_XY
165        class Pt3D hinge_rot; ///< hinge orientation (relative to part1) for HINGE_X and HINGE_XY
166        double hinge_limit_x[2], hinge_limit_y[2]; ///< hinge movement range - i.e., extreme allowed values of a state. A state can be an angle or other physical property. limit[0] <= 0 <= limit[1]. Relative Part position and orientation as set in the Model define the initial state 0.
167
168        Joint();
169        Joint(const Joint &src) :PartBase(getDefaultStyle()) { operator=(src); }
170        void operator=(const Joint &src);
171
172        /** connect two parts with this joint.
173                p2 position will be adjusted if delta option is in effect.
174                @see isDelta()
175                */
176        void attachToParts(Part *p1, Part *p2);
177        /// @see attachToParts(Part*,Part*)
178        void attachToParts(int p1, int p2);
179
180        /** discard delta information but don't disable delta flag.
181                delta will be calculated from parts positions during final consistency check.
182                */
183        void resetDelta();
184
185        /** enable or disable delta option.
186                delta value is not changed.
187                */
188        void useDelta(bool use);
189
190        /** @return true if delta option is in effect.
191                @see useDelta(), resetDelta(), useDelta()
192                */
193        bool isDelta();
194
195        /// ParamInterface object is preferred way to get/set other properties.
196        Param extraProperties();
197        Param properties();
198
199        // do not touch these:
200        paInt refno; ///< this joint's reference number
201        double stamina;
202        double stif, rotstif;   ///< stiffness for moving and bending forces
203        class Orient o; ///< orientation delta between parts as rotation matrix
204        /** flag: generated f0 should include delta data.
205                set by 'singlestep' if j: attributes use delta option */
206        bool usedelta;
207        Pt3D vcolor;
208
209        static Param &getStaticParam();
210};
211
212#define JOINT_DELTA_MARKER 99999.0
213
214
215
216////////////////// Neural Network /////////////////
217
218class NeuroClass;
219
220typedef UserTags<NeuroClass, void *, 5> NeuroClassUserTags;
221
222/** Information about neuron class.
223 */
224class NeuroClass
225{
226        bool ownedvectordata;
227        void operator=(const NeuroClass &nosuchthich) {}
228public:
229        SString name, longname, description;
230        ParamEntry *props;
231        bool ownedprops;//< destructor will free props using ParamObject::freeParamTab
232        paInt prefinputs, prefoutput;
233        paInt preflocation;
234
235        enum PrefLocation { PREFER_UNATTACHED = 0, PREFER_PART = 1, PREFER_JOINT = 2 };
236
237        static constexpr int SUPPORTED_SHAPETYPE_BALL_AND_STICK = 1;
238        static constexpr int SUPPORTED_SHAPETYPE_SOLIDS = 2;
239        static constexpr int SUPPORTED_SHAPETYPE_ALL = SUPPORTED_SHAPETYPE_BALL_AND_STICK | SUPPORTED_SHAPETYPE_SOLIDS;
240        paInt supported_shape_types; //< bitfield of 'Model::shape' values: NeuroClass::SUPPORTED_SHAPE_xxx = 1 << Model::SHAPE_xxx
241
242        static constexpr int SUPPORTED_JOINT_STICK = 1;
243        static constexpr int SUPPORTED_JOINT_FIXED = 2;
244        static constexpr int SUPPORTED_JOINT_HINGE_X = 4;
245        static constexpr int SUPPORTED_JOINT_HINGE_XY = 8;
246        static constexpr int SUPPORTED_JOINTSHAPE_ALL = SUPPORTED_JOINT_STICK + SUPPORTED_JOINT_FIXED + SUPPORTED_JOINT_HINGE_X + SUPPORTED_JOINT_HINGE_XY;
247        paInt supported_joint_shapes; //< bitfield of 'Joint::shape' values: NeuroClass::SUPPORTED_JOINT_xxx = 1 << JOINT::SHAPE_xxx
248
249        int *vectordata;
250        paInt visualhints;
251
252        //void *impl;
253        int impl_count;
254        bool active;
255        bool genactive;
256        NeuroClassUserTags userdata;
257
258        //////////////////////
259        ~NeuroClass();
260        NeuroClass();
261        NeuroClass(ParamEntry *_props, SString _description,
262                int _prefinputs, int _prefoutput, int _preflocation, int *_vectordata, bool own_vd = 1, int vhints = 0, int sup_shapes = NeuroClass::SUPPORTED_SHAPETYPE_ALL, int sup_joints = NeuroClass::SUPPORTED_JOINTSHAPE_ALL);
263        /** class name for use in Neuro::setClassName(), Neuro::setDetails() (former 'moredata' field),
264                eg. "N","-",G" */
265        const SString &getName() { return name; }
266        /** human friendly name, eg. "Neuron","Link","Gyroscope"  */
267        const SString &getLongName() { return longname; }
268        /** long description */
269        const SString &getDescription() { return description; }
270        ParamEntry *getParamTab() { return props; }
271
272        /** NeuroClass specific properties, recognized by all neurons of this class */
273        Param getProperties() { return Param(props); }
274
275        /** preferred number of inputs, -1 = no preference (any number will go).
276                extra inputs may be ignored by the object (depends on the class).
277                */
278        int getPreferredInputs() { return (int)prefinputs; }
279
280        /** @return 0 if this object doesn't provide useful output signal. */
281        int getPreferredOutput() { return (int)prefoutput; }
282
283        /** @return 0 (PREFER_UNATTACHED) if the neuron doesn't need any assignment to a body element.
284                @return 1 (PREFER_PART) if the neuron likes to be attached to a Part ( @see Neuro::attachToPart() )
285                @return 2 (PREFER_JOINT) if the neuron prefers to be attached to a Joint ( @see Neuro::attachToJoint() )
286                */
287        PrefLocation getPreferredLocation() { return (PrefLocation)preflocation; }
288        /** vector drawing to be used in neuro net diagram.
289                interpretation:
290                {
291                LEN = datalength (excluding this number)
292                NL = number_of_lines
293                line#1 ->  NS = number_of_segments, x1,y1, x2,y2, ... xNS-1,yNS-1,
294                ...
295                line#NL -> NS = number_of_segments, x1,y1, x2,y2, ... xNS-1,yNS-1,
296                }
297                */
298        int getSupportedShapeTypes() { return (int)supported_shape_types; }
299        bool isShapeTypeSupported(ModelEnum::ShapeType t) { return (1 << (int)t) & supported_shape_types; }
300        int getSupportedJointShapes() { return (int)supported_joint_shapes; }
301        bool isJointShapeSupported(Joint::Shape t) { return (1 << (int)t) & supported_joint_shapes; }
302        int *getSymbolGlyph()
303        {
304                return vectordata;
305        }
306        void setSymbolGlyph(int *data, bool owned = 1)
307        {
308                if (vectordata && ownedvectordata) delete[]vectordata;
309                vectordata = data; ownedvectordata = owned;
310        }
311        /** additional information about how the neuron should be drawn
312                used by structure view (and maybe some other components).
313                return value is defined by the enum Hint
314                @see enum Hint
315                */
316        int getVisualHints()
317        {
318                return (int)visualhints;
319        }
320
321        enum Hint
322        {
323                /** don't draw neurons of this class */
324                Invisible = 1,
325                /** don't draw classname label below the neuron */
326                DontShowClass = 2,
327                /** draw the neuron at the first part when attached to joint (default is in the middle) */
328                AtFirstPart = 4,
329                /** draw the neuron at the second part when attached to joint (default is in the middle) */
330                AtSecondPart = 8,
331                /** use effector colour for this neuro unit */
332                EffectorClass = 16,
333                /** use receptor colour for this neuro unit */
334                ReceptorClass = 32,
335                IsV1BendMuscle = 64,
336                IsV1RotMuscle = 128,
337                IsLinearMuscle = 256,
338                IsSolidMuscle = 512
339        };
340
341        /** textual summary, automatically generated from other properties (like the neuro class tooltip) */
342        SString getSummary();
343
344        static void resetActive(); ///< set default values of active and genactive for all classes
345        static void setGenActive(const char *classes[]); ///< set genactive for specified classes
346};
347
348
349
350
351
352
353/** Single processing unit in Framsticks neural network.  */
354class Neuro : public PartBase
355{
356        friend class Model;
357        static SString getDefaultStyle();
358
359        struct NInput {
360                Neuro *n; double weight; SString *info;
361                NInput(Neuro *_n, double w, SString *i = 0) :n(_n), weight(w), info(i) {}
362        };
363
364        SListTempl<NInput> inputs;
365
366        NeuroClass *myclass;
367        bool knownclass;
368        SString myclassname, myclassparams;
369        /** set myclass and make knownclass=true */
370        void checkClass();
371        SString **inputInfo(int i);
372        void defassign();
373
374public:
375        enum NeuroFlags { HoldState = 2 };
376        Param properties();
377        Param extraProperties();
378
379        void setInputInfo(int i, const SString &name, const SString &value);
380        void setInputInfo(int i, const SString &name, int value);
381        void setInputInfo(int i, const SString &name, double value);
382        SString getInputInfo(int i);
383        SString getInputInfo(int i, const SString &name);
384
385        NeuroClass *getClass();
386        void setClass(NeuroClass *);
387
388        SString getClassParams() { return myclassparams; }
389        void setClassParams(const SString &cp) { myclassparams = cp; }
390
391        SString getClassName();
392        void setClassName(const SString &clazz);
393
394        /** return neuro unit details encoded as <CLASS> ":" <PROPERTIES>
395
396                new Neuro can be created as root object (without parent) or can be
397                the child of existing Neuro. Children of the Neuro are its inputs.
398                Standard Framsticks neuron calculates the sum of all input units - other processing
399                units don't have to treat them equally and can even ignore some of them.
400                There are hints about expected inputs in the class database, @see getClass
401
402                Application should not assume anything about classes and its properties
403                except for two standard classes: (information about all current classes
404                can be retrieved with getClass/getClassProperties methods)
405                - getClassName()="N" is the standard Framsticks neuron, accepts any number of inputs,
406                compatible with old Neuro object
407                - getClassName()="-" is the neuron link, compatible with old Neuro-Neuro link
408                (NeuroItem with empty details)
409                Empty details defaults to "-" if the parent unit is specified,
410                and "N" if the unit has no parent.
411                */
412        SString getDetails();
413
414        /** details = classname + ":" + classparams
415                @see getDetails()
416                */
417        void setDetails(const SString &);
418
419#define STATRICKCLASS Neuro
420        PARAMGETDEF(details) { arg1->setString(getDetails()); }
421        PARAMSETDEF(details) { setDetails(arg1->getString()); return PSET_CHANGED; }
422        PARAMGETDEF(inputCount);
423        PARAMPROCDEF(p_getInputNeuroDef);
424        PARAMPROCDEF(p_getInputNeuroIndex);
425        PARAMPROCDEF(p_getInputWeight);
426        PARAMGETDEF(classObject);
427#undef STATRICKCLASS
428
429        ///@param handle_defaults_when_saving see SyntParam
430        SyntParam classProperties(bool handle_defaults_when_saving = true);
431        // base properties:
432        paInt refno; ///< unique reference number (former 'neuro' refno)
433
434        paInt part_refno; ///< can be used by some items as the part ref#
435        paInt joint_refno; ///< can be used by some items as the joint ref#
436
437        Pt3D pos, rot;  ///< default = zero
438
439        ModelUserTags userdata;
440
441        Neuro();
442        Neuro(double _state, double _inertia, double _force, double _sigmo);
443        Neuro(const Neuro &src) :PartBase(getDefaultStyle()) { operator=(src); }
444
445        ~Neuro();
446
447        void operator=(const Neuro &src);
448
449        /** Attach this Neuro to the specified Part or detach it from the body if p==NULL.
450                Neuro can be attached to either Part or Joint, but not both.
451                @see getPart()
452                */
453        void attachToPart(Part *p) { part = p; joint = 0; }
454
455        /** Attach this Neuro to the specified Joint or detach it from the body if p==NULL.
456                Neuro can be attached to either Part or Joint, but not both.
457                @see getJoint()
458                */
459        void attachToJoint(Joint *j) { joint = j; part = 0; }
460
461        void attachToPart(int i);
462        void attachToJoint(int i);
463
464        /** @return Part the Neuro is attached to, or NULL if it has no defined location on the body.
465                @see attachToPart()
466                */
467        Part *getPart() { return part; }
468
469        /** @return Joint the Neuro is attached to, or NULL if it has no defined location on the body.
470                @see attachToJoint()
471                */
472        Joint *getJoint() { return joint; }
473
474        int isOldEffector();
475        int isOldReceptor();
476        int isOldNeuron();
477        int isNNConnection();
478
479        /** @return the number of inputs connected to this Neuro.
480                Functions like getInput(), getInputWeight() will accept connection number [0..InputCount-1]
481                */
482        int getInputCount() const { return inputs.size(); }
483
484        /// @return the number of output connections (including possible self-connections)
485        int getOutputsCount() const;
486
487        /** @return the Neuro connected as i-th input */
488        Neuro *getInput(int i) const { return (i >= inputs.size()) ? 0 : inputs(i).n; }
489        /** @return the Neuro connected as i-th input.
490                @param weight
491                */
492        Neuro *getInput(int i, double &weight) const;
493        /** @return connectin weight for i-th input */
494        double getInputWeight(int i) const;
495        /** change connection weight for i-th input */
496        void setInputWeight(int i, double weight);
497        /** connect i-th input with another neuron */
498        void setInput(int i, Neuro *n);
499        /** connect i-th input with another neuron */
500        void setInput(int i, Neuro *n, double weight);
501        /** add new input. @return its reference number */
502        int addInput(Neuro *child, double weight = 1.0, const SString *info = 0);
503        /** @return reference number [0..InputCount-1] of the input
504           or -1 if 'child' is not connected with this Neuro.*/
505        int findInput(Neuro *child) const;
506        void removeInput(paInt refno);
507        /**    @return reference number of the child connection, like findInput() */
508        int removeInput(Neuro *child);
509
510        int findInputs(SList &result, const char *classname = 0, const Part *part = 0, const Joint *joint = 0) const;
511        int findOutputs(SList &result, const char *classname = 0, const Part *part = 0, const Joint *joint = 0) const;
512
513        /* class database retrieval */
514        static int getClassCount();
515        /** @return Neuro class name.
516                @param classindex 0 .. getClassCount()
517                */
518        static SString getClassName(int classindex);
519        static NeuroClass *getClass(int classindex);
520        static NeuroClass *getClass(const SString &classname);
521        static int getClassIndex(const NeuroClass *nc);
522
523        // not really private, but you should not access those directly
524        double state;
525
526        /** may reference parent neuron if parentcount is exacty 1. parent is invalid otherwise. @sa parentcount */
527        Neuro *parent;
528        int parentcount; ///< @sa parent
529
530        Part *part;     ///< link to the Part
531        Joint *joint;   ///< link to the Joint - required by some objects (eg.muscles)
532        Orient o;       ///< rotation matrix calculated from "rot"
533        static ParamEntry emptyParamTab[];
534        static Param &getStaticParam();
535};
536
537class NeuroExt : public Neuro
538{
539public:
540#define STATRICKCLASS NeuroExt
541        PARAMGETDEF(neuroclass);
542        PARAMSETDEF(neuroclass);
543        PARAMGETDEF(liveNeuro);
544#undef STATRICKCLASS
545        static ParamEntry *getParamTab();
546};
547
548class NeuroConn
549{
550        void defassign();
551public:
552        int n1_refno, n2_refno;
553        double weight;
554        SString info;
555        NeuroConn();
556};
557
558extern ParamEntry f0_part_paramtab[], f0_part_minmaxdef_paramtab[], f0_joint_paramtab[], f0_nodeltajoint_paramtab[], f0_neuro_paramtab[], f0_neuroconn_paramtab[], f0_neuroitem_paramtab[];
559
560#endif
Note: See TracBrowser for help on using the repository browser.