Domains
Domains are mainly used when creating nets. There are different types that all inherit from the
Domain class, for example
or domains specified by their shape, for example
These classes are located in ddg.datastructures.nets.domain.
Creating a Smooth Domain
A smooth domain is given by an interval for each of its direction.
>>> import ddg
>>> import numpy as np
>>> domain = ddg.nets.SmoothDomain([[0, 4], [-np.pi, np.pi]])
>>> domain.bounded
True
>>> domain.intervals
[[0.0, 4.0], [-3.141592653589793, 3.141592653589793]]
>>> domain.periodicity
set()
Note that SmoothDomain always expects a list of intervals to be passed to it,
even if the domain is supposed to be 1-dimensional. To circumvent this, use
SmoothInterval instead:
>>> import ddg
>>> domain = ddg.nets.SmoothInterval([0.0, 4.0])
>>> domain.interval
[0.0, 4.0]
You can also create both unbounded and periodic domains.
>>> import ddg
>>> import numpy as np
>>> domain = ddg.nets.SmoothDomain([[-np.inf, 0], [0, 2]])
>>> domain.bounded
False
>>> domain.unbounded_directions
[0]
To mark a direction as periodic, simply add True as the third entry of its interval:
>>> import ddg
>>> domain = ddg.nets.SmoothDomain([[0, 3], [0, 2 * np.pi, True]])
>>> domain.periodicity
{1}
Creating a Discrete Domain
A DiscreteDomain behaves very much the same as a smooth one.
They have all properties of a smooth domain, but additionally contain the combinatorial
information of themselves, i.e. their edge and face data. These are used primarily in the
conversion to a blender mesh.
>>> import ddg
>>> domain = ddg.nets.DiscreteDomain([[0, 4], [-1, 10]])
>>> domain.intervals
[[0, 4], [-1, 10]]
Moreover we can iterate over all points inside of a discrete domain with the help of its traverser:
>>> import ddg
>>> domain = ddg.nets.DiscreteDomain([[0, 1], [0, 1]])
>>> for i in domain.traverser:
... print(i)
...
(0, 0)
(0, 1)
(1, 0)
(1, 1)
>>> for i in domain.traverser:
... print(domain.traverser.idx(*i))
...
0
1
2
3
>>> for i in domain.edge_data:
... print(i)
...
(0, 2)
(0, 1)
(1, 3)
(2, 3)
>>> for i in domain.face_data:
... print(i)
...
(0, 2, 3, 1)
Modifying a Domain
A domain can be modifyied by utility functions located at ddg.datastructures.nets.utils.
Possibilities are
Sampling
Sampling a SmoothDomain means choosing finitely (or countably) many of its points to obtain a discrete object.
Pyddg provides the function sample_smooth_domain.
It takes a SmoothDomain and returns a DiscreteNet.
The resulting DiscreteNet corresponds to a function \(\phi: K \to U\), where \(K \subseteq \mathbb{Z}^n\)
is a discrete set of points, and \(U \subseteq \mathbb{R}^n\) is the SmoothDomain.
We restrict ourselves to rectangular domains. With this we can think of \(\phi\) as a map that places the grid \(K\) on (a subset of) \(U\) (see figures below).
Sampling Syntax
See the docstring of sample_smooth_domain for a full description of the syntax.
The general syntax of a sampling is a list with entries [value, option], which specify the sampling in each direction.
Here, value is an int or a float and option is the sampling option (see below).
Exceptions are the option compound sampling 'c', where the syntax is [float, int, option] and
stepsize '', where the sampling can be given as a single argument float.
Note
If we only specify one sampling option in sample_smooth_domain, e.g. [0.5, ''] or 0.5, it is applied to every direction of the domain.
Sampling Options
The following table gives an overview of the available options for the sampling argument of
sample_smooth_domain or sample_smooth_net.
For a detailed explanation of the options, see below.
options |
‘’ |
‘t’ |
‘c’ |
‘s’ |
|---|---|---|---|---|
meaning |
stepsize |
total |
compound |
symmetric |
bounded |
yes |
yes |
yes |
yes |
unbounded |
yes |
no |
yes |
no |
type |
float |
int |
float, int |
float |
periodic |
can preserve |
preserves |
preserves |
can preserve |
anchor |
yes |
no |
no |
no |
Consider the smooth domain \([0, 0.8]\times[0, 1.3]\).
Smooth domain \([0, 0.8]\times[0, 1.3]\)
total amount of samples
One way to define a sampling is to divide each direction of the domain into \(n\) even pieces.
Let us divide \([0, 0.8]\) into 5 and \([0, 1.3]\) into 9 even pieces.
This is done by using the sampling option 't' (“total number of samplings”) in each case.
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, 0.8], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(
... smoothdomain, [[5, "t"], [9, "t"]]
... )
Sampled with sampling:
[[5, 't'], [9, 't']]
If the domain is unbounded in a direction, the option 't' will not work,
since it divides the direction into even pieces. For unbounded directions, you can use the
step size option.
step size
Since this option works for all domains, it is the default that sample_smooth_domain uses,
if none is specified. In this case, we can
pass a float for each direction:
sample_smooth_domain(smoothdomain, [.1, .2])choose the option
'':sample_smooth_domain(smoothdomain, [[.1, ''], [.2,'']])
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, 0.8], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, [0.1, ""])
Sampled with sampling:
[0.1, '']
If the step size is chosen so that it does not divide the length of the domain, the sampling will not cover the entire domain:
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, 0.8], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, 0.3)
Sampled with sampling:
0.3
Depending on the tolerances we have passed to sample_smooth_domain the resulting discrete domain
would be or would not be periodic if our smooth domain was. (more on periodicity below)
symmetric sampling
Symmetric sampling can be chosen with option 's' and a given step size.
sample_smooth_domain then attempts to map the samples into the smooth domain symmetrically.
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, 0.8], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, [0.3, "s"])
Sampled with sampling:
[0.3, 's']
anchor
Sometimes we want a specific point of our smooth domain to be a sample. For this we can use the anchor keyword argument. It is only compatible with step size and symmetric sampling.
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, 0.8], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(
... smoothdomain, 0.2, anchor=(0.25, 0.35)
... )
Sampled with sampling:
0.2, anchor=(0.25, 0.35)
compound sampling
Compound samping can be chosen with 'c'. It combines the sampling forms ‘step size’ and ‘total
amount’ and is given in the form [stepsize, total, 'c']. For unbounded directions it generates a total number of samples
stepsize apart, starting from either the lower bound/upper bound or 0 (depending on whether the direction is bounded
from below/above or not at all). In a bounded direction, it behaves as the option 't' would.
>>> import numpy as np
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = net.domain.SmoothDomain([[0, np.inf], [0, 1.3]])
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, [0.1, 11, "c"])
>>> sampled.domain
DiscreteRectangularDomain([0, 10], [0, 10])
periodicity
Periodic smooth domains appear for example in closed curves or surfaces of revolution. Since the domain has to be bounded in the direction that it is periodic in, all sampling options can be applied to the direction. However:
Note
Not all sampling options will preserve periodicity. (See Sampling Options)
That is, in some cases the sampling of a periodic smooth domain will be a non-periodic discrete domain:
>>> import numpy as np
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = ddg.nets.SmoothDomain(
... [
... [0, 1, True],
... ]
... )
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, 0.3)
>>> sampled.domain.periodic
False
The periodicity breaks for two reasons:
The step size is too large: it caused the last sample to not end up close enough to the first one
(close in terms of atol passed to sample_smooth_domain)
The step size does not divide the length of the direction: if the stepsize divides the length of the direction, it will fit perfectly, no matter how large
Fix either of these two points to preserve periodicity:
>>> import numpy as np
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = ddg.nets.SmoothDomain(
... [
... [0, 1, True],
... ]
... )
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, 0.10000000000000001)
>>> sampled.domain.periodic
True
>>> import numpy as np
>>> import ddg
>>> import ddg.datastructures.nets as net
>>> smoothdomain = ddg.nets.SmoothDomain(
... [
... [0, 1, True],
... ]
... )
>>> sampled = net.conversion.sample_smooth_domain(smoothdomain, 0.5)
>>> sampled.domain.periodic
True
Conversion to Half-edge
Bounded discrete domains of dimension 2 or less can be converted to a
The half-edge data structure object using the function
ddg.conversion.halfedge.nets.discrete_domain_to_halfedge(). The
vertices of the resulting halfedge object will have an attribute co
containing the value from domain.traverser and the order will be the same.
>>> from ddg.conversion.halfedge.nets import discrete_domain_to_halfedge
>>> domain = ddg.nets.DiscreteDomain([[0, 1], [0, 1]])
>>> surface = discrete_domain_to_halfedge(domain)
>>> for c in domain.traverser:
... print(c)
...
(0, 0)
(0, 1)
(1, 0)
(1, 1)
>>> for v in surface.verts:
... print(v.co)
...
(0, 0)
(0, 1)
(1, 0)
(1, 1)
The name of the coordinate attribute can be changed using the co_attr
argument of the conversion function.