Density Function Types
A density function type represents a kind of operation, which to apply on its arguments. In Rhombus, the subclasses of DensityFunction are the nodes of the abstract syntax tree that represents a density function.
Defining DensityFunction classes
In order for the data for new density function to be stored, a dataclass inheriting from DensityFunction is needed.
The new class requires the following attributes:
id: ClassVar[str]
# The identifier of the density function type including its namespace,
# like it is used in the `type` field of a density function definition in a datapack.
decode: ClassVar[Callable[[type[Self], JSONDict], Self]]
# A `classmethod` that can create an instance of the class
# from a dictionary(JSON density function definition)
encode: ClassVar[Callable[[Self], JSONDict | float | str]]
# A method that can create a dictionary (JSON density function definition)
# from an instance of the class
Base classes
There are some base classes that already implement the decode and encode methods for common fields like shown as follows.
Types with no arguments or one or two density function arguments
There are base classes for density function types that take no arguments or one or two arguments that are density functions. When inheriting from these, besides stating the function type id, no additional declarations have to be made. The classes are:
SimpleFunctionBasefor function types that don't take any argumentsMappedFunctionBasefor function types that take an argumentargumentof typeDensityFunctionDoubleArgumentFunctionBasefor function types that take two argumentsargument1andargument2of typeDensityFunction
class end_islands(SimpleFunctionBase):
id: ClassVar[str] = "minecraft:end_islands"
class abs(MappedFunctionBase):
id: ClassVar[str] = "minecraft:abs"
class add(DoubleArgumentFunctionBase):
id: ClassVar[str] = "minecraft:add"
Types with any fields of trivial types
For density function types with fields that aren't covered by the base classes above, a dataclass declaration inheriting from MultiArgumentsFunctionBase has to be made.
The following types are supported in the fields:
- JSON-compatible value types (
str,dict,list,tuple,str,int,float,bool,None) DensityFunctionlist[DensityFunction]- Classes inheriting from
DatapackResource(likeNoise)
Fields of DensityFunction subclasses must be called exactly like their counterparts in the JSON definition, unless a separate encoding and decoding logic is implemented.
@dataclass # See how the dataclass decorator is required here,
# because we declare only here what fields the function type has
class shifted_noise(MultiArgumentsFunctionBase):
id: ClassVar[str] = "minecraft:shifted_noise"
noise: Noise
xz_scale: float
y_scale: float
shift_x: DensityFunction
shift_y: DensityFunction
shift_z: DensityFunction
When a new class inheriting DensityFunction is defined, it will automatically be registered in the decoding context.
Types with more complex fields
Density function types that require fields of non-trivial types or whose fields should have a different composition than the JSON definition, the required methods have to be implemented manually.
@dataclass
class spline(DensityFunction):
id: ClassVar[str] = "minecraft:spline"
coordinate: DensityFunction
points: list[tuple[float, DensityFunction, float]]
@classmethod
def decode(cls, data: JSONDict) -> spline:
return cls(
decode_HOLDER_HELPER_CODEC(data["spline"]["coordinate"]),
[
(point["location"], decode_HOLDER_HELPER_CODEC(point["value"]), point["derivative"])
for point in data["spline"]["points"]
]
)
def encode(self) -> JSONDict:
return {
"type": self.id,
"spline": {
"coordinate": self.coordinate.encode(),
"points": [
{
"location": point[0],
"value": point[1].encode() if isinstance(point[1], DensityFunction) else point[1],
"derivative": point[2],
}
for point in self.points
]
}
}