def expand_object(self, obj, recursive=False): ensure_type(obj, c4d.BaseObject) mg = obj.GetMg() if isinstance(obj, c4d.PointObject): for p in obj.GetAllPoints(): self.expand(p * mg) else: mp = obj.GetMp() bb = obj.GetRad() V = c4d.Vector self.expand(V(mp.x + bb.x, mp.y + bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y + bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y + bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y + bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y - bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y - bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y - bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y - bb.y, mp.z - bb.z) * mg) if recursive: for child in obj.GetChildren(): self.expand_object(child, True)
def vbbmid(vectors): r""" Returns the mid-point of the bounding box spanned by the list of vectors. This is different to the arithmetic middle of the points. *Changed in 1.3.0*: - Moved to :mod:`c4dtools.math`. - Raises *ValueError* if *vectors* is not a sequence type or is empty. Returns: :class:`c4d.Vector` """ ensure_type(vectors, collections.Sequence, name='vectors') vectors = tuple(vectors) if not vectors: raise ValueError('An empty sequence is not accepted.') min = Vector(vectors[0]) max = Vector(min) for v in vectors: vmin(min, v) vmax(max, v) return (min + max) * 0.5
def expand_object(self, obj, recursive=False): ensure_type(obj, c4d.BaseObject) mg = obj.GetMg() if isinstance(obj, c4d.PointObject): for p in obj.GetAllPoints(): self.expand(p * mg) else: mp = obj.GetMp() bb = obj.GetRad() V = c4d.Vector self.expand(V(mp.x + bb.x, mp.y + bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y + bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y + bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y + bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y - bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y - bb.y, mp.z + bb.z) * mg) self.expand(V(mp.x + bb.x, mp.y - bb.y, mp.z - bb.z) * mg) self.expand(V(mp.x - bb.x, mp.y - bb.y, mp.z - bb.z) * mg) if recursive: for child in obj.GetChildren(): self.expand_object(child, True)
def __init__(self, p, d): r""" *New in 1.3.0*. Initialize the line with one point on the line and its direction vector. Both, *p* and *d* may be iterables. In this case, they must be iterables with one to three elements because they will be extracted to the :class:`c4d.Vector` constructor. The direction vector is normalized. The vectors are copied internally. :param p: An element of the line (:class:`c4d.Vector`) :param d: The direction vector of the line (:class:`c4d.Vector`) """ if isinstance(p, collections.Sequence): p = Vector(*p) if isinstance(d, collections.Sequence): d = Vector(*d) ensure_type(p, Vector, name='p') ensure_type(d, Vector, name='d') self.p = Vector(p) self.d = d.GetNormalized()
def load_container(self, dialog, container, genlist=False): r""" Just like :meth:`load_dict` but *container* is a :class:`c4d.BaseContainer` instance. If *genlist* is True, a list of names is generated that have been set from the passed *data*. """ ensure_type(container, BaseContainer) # Rework the data dictionary to associate the symbols # with the datatypes. data = {} for name, (symbol, datatype, default) in self._data.iteritems(): data[symbol] = datatype, name names = [] for symbol, value in container: try: datatype, name = data[symbol] except KeyError: continue value = datatype.from_container(symbol, container) datatype.set_value(dialog, symbol, value) if genlist: names.append(name) return names
def vbbmid(vectors): r""" Returns the mid-point of the bounding box spanned by the list of vectors. This is different to the arithmetic middle of the points. *Changed in 1.3.0*: - Moved to :mod:`c4dtools.math`. - Raises *ValueError* if *vectors* is not a sequence type or is empty. Returns: :class:`c4d.Vector` """ ensure_type(vectors, collections.Sequence, name='vectors') vectors = tuple(vectors) if not vectors: raise ValueError('An empty sequence is not accepted.') min = Vector(vectors[0]) max = Vector(min) for v in vectors: vmin(min, v) vmax(max, v) return (min + max) * 0.5
def __init__(self, p, d): r""" *New in 1.3.0*. Initialize the line with one point on the line and its direction vector. Both, *p* and *d* may be iterables. In this case, they must be iterables with one to three elements because they will be extracted to the :class:`c4d.Vector` constructor. The direction vector is normalized. The vectors are copied internally. :param p: An element of the line (:class:`c4d.Vector`) :param d: The direction vector of the line (:class:`c4d.Vector`) """ if isinstance(p, collections.Sequence): p = Vector(*p) if isinstance(d, collections.Sequence): d = Vector(*d) ensure_type(p, Vector, name='p') ensure_type(d, Vector, name='d') self.p = Vector(p) self.d = d.GetNormalized()
def __init__(self, fields, op, do_caching=True): super(UserDataSetAndGet, self).__init__() ensure_type(fields, dict) ensure_type(op, c4d.BaseList2D) self._fields = copy.copy(fields) self._op = op self._cache = {} self._do_caching = do_caching
def __init__(self, fields, op, do_caching=True): super(UserDataSetAndGet, self).__init__() ensure_type(fields, dict) ensure_type(op, c4d.BaseList2D) self._fields = copy.copy(fields) self._op = op self._cache = {} self._do_caching = do_caching
def register_datatype(self, datatype): r""" Register a new data-type that will be available for the :meth:`add` method. """ ensure_type(datatype, Datatype) if not datatype.identifier: raise ValueError('passed Datatype instance has not identifier set.') if datatype.identifier in self._types: raise RuntimeError('passed Datatype identifier already occupied.') self._types[datatype.identifier] = datatype
def load_dict(self, dialog, data, genlist=False): r""" Set values from a Python dictionary to the dialog. If *genlist* is True, a list of names is generated that have been set from the passed *data*. """ ensure_type(data, dict) names = [] for name, value in data.iteritems(): symbol, datatype, default = self._data[name] datatype.set_value(dialog, symbol, value) if genlist: names.append(name) return names
def intersection(self, line, precision=PRECISION): r""" *New in 1.3.0*. Calculates the intersection point of the calling and the passed *line*. :param line: The complementary line to check for intersection with the calling line. :param precision: The allowed deviation between the components of the linear factors. :return: :class:`c4d.Vector` if the lines intersect, ``None`` if they do not. .. seealso:: :func:`line_intersection` """ ensure_type(line, Line, name='line') return line_intersection(self.p, self.d, line.p, line.d, precision)
def intersection(self, line, precision=PRECISION): r""" *New in 1.3.0*. Calculates the intersection point of the calling and the passed *line*. :param line: The complementary line to check for intersection with the calling line. :param precision: The allowed deviation between the components of the linear factors. :return: :class:`c4d.Vector` if the lines intersect, ``None`` if they do not. .. seealso:: :func:`line_intersection` """ ensure_type(line, Line, name='line') return line_intersection(self.p, self.d, line.p, line.d, precision)
def add_symbols(self, symbols): r""" Add the dictionary *symbols* to the resources symbols. Raises: TypeError if *symbols* is not a dict instance. KeyError if a key in *symbols* is already defined in the resource and their value differs. """ utils.ensure_type(symbols, dict) res_symbols = self.symbols for key, value in symbols.iteritems(): utils.ensure_type(key, basestring, name='dict-key') utils.ensure_type(value, int, name='dict-value') if key in res_symbols and res_symbols[key] != value: msg = 'key %r already defined in the resource and ' \ 'the value differs from the updating symbols.' raise KeyError(msg % key) if value > self.highest_symbol: self.highest_symbol = value res_symbols.update(symbols)
def add_symbols(self, symbols): r""" Add the dictionary *symbols* to the resources symbols. Raises: TypeError if *symbols* is not a dict instance. KeyError if a key in *symbols* is already defined in the resource and their value differs. """ utils.ensure_type(symbols, dict) res_symbols = self.symbols for key, value in symbols.iteritems(): utils.ensure_type(key, basestring, name='dict-key') utils.ensure_type(value, int, name='dict-value') if key in res_symbols and res_symbols[key] != value: msg = 'key %r already defined in the resource and ' \ 'the value differs from the updating symbols.' raise KeyError(msg % key) if value > self.highest_symbol: self.highest_symbol = value res_symbols.update(symbols)
def prepare(filename=None, c4dres=None, cache=True, libfolder_name='lib', resfolder_name='res', parse_description=False, imp_store_modules=True): r""" Call this function from a Cinema 4D python plugin-file (``*.pyp``) to set up convenient data that can be used from the plugin. .. code-block:: python import c4d import c4dtools res, imp = c4dtools.prepare(__file__, __res__) # ... :param filename: Just pass the ``__file__`` variable from the plugins global scope. *New in 1.3.0*: Default value added. The filename will be retrieved using the globals of the frame that has called the function if *None* was passed. :param c4dres: The :class:`c4d.plugins.GeResource` instance from the plugin's scope. *New in 1.2.6*: Default value added. The plugin resource will be retrieved using the globals of the frame that has called the function if *None* was passed. :param cache: True by default. Defines wether the resource symbols will be cached. :param libfolder_name: The name of the folder the plugin related libraries are stored. The returned Importer instance will be able to load python modules and packages from this directory. :param resfolder_name: The name of the plugins resource folder. This usually does not need to be changed as the name of this folder is defined by Cinema 4D. :param parse_description: False by default. When True, description resource symbols will parsed additionally to the dialog resource symbols. Note that strings can *not* be loaded from symbols of description resources. :param imp_store_modules: Passed to the constructor of the returned :class:`Importer`, defining whether imported modules are stored or not. :return: A tuple of two elements: - :class:`c4dtools.resource.Resource` - :class:`c4dtools.utils.Importer` *New in 1.3.0*: Added *imp_store_modules* parameter. """ globals_ = sys._getframe().f_back.f_globals if filename is None: filename = globals_.get('__file__', None) if not filename: raise ValueError('filename could not be retrieved from the ' 'calling frame.') if c4dres is None: c4dres = globals_.get('__res__', None) utils.ensure_type(filename, basestring, name='filename') utils.ensure_type(c4dres, c4d.plugins.GeResource, type(None), name='c4dres') path = helpers.Attributor() path.root = os.path.dirname(filename) path.res = resfolder_name path.lib = libfolder_name if not os.path.isabs(path.res): path.res = os.path.join(path.root, path.res) if not os.path.isabs(path.lib): path.lib = os.path.join(path.root, path.lib) imp = importer.Importer(store_modules=imp_store_modules) if os.path.isdir(path.lib): imp.add(path.lib) res = resource.Resource.from_resource_folder(path.res, c4dres, cache, parse_description) return (res, imp)
def prepare(filename=None, c4dres=None, cache=True, libfolder_name='lib', resfolder_name='res', parse_description=False, imp_store_modules=True): r""" Call this function from a Cinema 4D python plugin-file (``*.pyp``) to set up convenient data that can be used from the plugin. .. code-block:: python import c4d import c4dtools res, imp = c4dtools.prepare(__file__, __res__) # ... :param filename: Just pass the ``__file__`` variable from the plugins global scope. *New in 1.3.0*: Default value added. The filename will be retrieved using the globals of the frame that has called the function if *None* was passed. :param c4dres: The :class:`c4d.plugins.GeResource` instance from the plugin's scope. *New in 1.2.6*: Default value added. The plugin resource will be retrieved using the globals of the frame that has called the function if *None* was passed. :param cache: True by default. Defines wether the resource symbols will be cached. :param libfolder_name: The name of the folder the plugin related libraries are stored. The returned Importer instance will be able to load python modules and packages from this directory. :param resfolder_name: The name of the plugins resource folder. This usually does not need to be changed as the name of this folder is defined by Cinema 4D. :param parse_description: False by default. When True, description resource symbols will parsed additionally to the dialog resource symbols. Note that strings can *not* be loaded from symbols of description resources. :param imp_store_modules: Passed to the constructor of the returned :class:`Importer`, defining whether imported modules are stored or not. :return: A tuple of two elements: - :class:`c4dtools.resource.Resource` - :class:`c4dtools.utils.Importer` *New in 1.3.0*: Added *imp_store_modules* parameter. """ globals_ = sys._getframe().f_back.f_globals if filename is None: filename = globals_.get('__file__', None) if not filename: raise ValueError('filename could not be retrieved from the ' 'calling frame.') if c4dres is None: c4dres = globals_.get('__res__', None) utils.ensure_type(filename, basestring, name='filename') utils.ensure_type(c4dres, c4d.plugins.GeResource, type(None), name='c4dres') path = helpers.Attributor() path.root = os.path.dirname(filename) path.res = resfolder_name path.lib = libfolder_name if not os.path.isabs(path.res): path.res = os.path.join(path.root, path.res) if not os.path.isabs(path.lib): path.lib = os.path.join(path.root, path.lib) imp = importer.Importer(store_modules=imp_store_modules) if os.path.isdir(path.lib): imp.add(path.lib) res = resource.Resource.from_resource_folder(path.res, c4dres, cache, parse_description) return (res, imp)