A PPX deriver that generates functions for the spatial transformation of user defined abstract and record types composed of types for which said transformation functions are defined, in particular, the types of the OCADml library (*e.g.* `V3.t`

and `V2.t`

), as well as CAD specific types such as `Scad.t`

of OSCADml.

`Ppx_deriving_cad`

-- ppx module interface

`@@deriving cad`

-- Derives`OCADml`

transformation functions for the derived type`@@deriving cad_jane`

-- same, but defaulting to janestreet conventions (see`@cad.mapf`

)

To generate the suite of basic transformation functions for your type, simply attach `[@@deriving cad]`

(or `[@@deriving cad_jane]`

) to the end.

**For example:**

```
open OCADml
open OSCADml
type mark =
{ scad : Scad.d3
; centre : V3.t
}
[@@deriving cad]
```

**Generates:**

```
val translate_mark : V3.t -> mark -> mark
val xtrans_mark : float -> mark -> mark
val ytrans_mark : float -> mark -> mark
val ztrans_mark : float -> mark -> mark
val rotate_mark : ?about:V3.t -> V3.t -> mark -> mark
val xrot_mark : ?about:V3.t -> float -> mark -> mark
val yrot_mark : ?about:V3.t -> float -> mark -> mark
val zrot_mark : ?about:V3.t -> float -> mark -> mark
val axis_rotate_mark : ?about:V3.t -> V3.t -> float -> mark -> mark
val quaternion_mark : ?about:V3.t -> Quaternion.t -> mark -> mark
val scale_mark : V3.t -> mark -> mark
val xscale_mark : float -> mark -> mark
val yscale_mark : float -> mark -> mark
val zscale_mark : float -> mark -> mark
val mirror_mark : V3.t -> mark -> mark
val affine_mark : Affine3.t -> mark -> mark
```

If the name of the type being derived is `t`

, then the functions generated (and those required to be present for the types inside of a type/record being derived) will be given unqualified names. For example, applying `[@@deriving cad]`

to a lone record type `t`

would give a module that adheres to the following signature.

```
open OCADml
open OSCADml
module Mark : sig
type t =
{ scad : Scad.d3
; centre : V3.t
}
val translate : V3.t -> t -> t
val xtrans : float -> t -> t
val ytrans : float -> t -> t
val ztrans : float -> t -> t
val rotate : ?about:V3.t -> V3.t -> t -> t
val xrot : ?about:V3.t -> float -> t -> t
val yrot : ?about:V3.t -> float -> t -> t
val zrot : ?about:V3.t -> float -> t -> t
val axis_rotate : ?about:V3.t -> V3.t -> float -> t -> t
val quaternion : ?about:V3.t -> Quaternion.t -> t -> t
val scale : V3.t -> t -> t
val xscale : float -> t -> t
val yscale : float -> t -> t
val zscale : float -> t -> t
val mirror : V3.t -> t -> t
val affine : Affine3.t -> t -> t
end = struct
type t =
{ scad : Scad.d3
; centre : V3.t
}
[@@deriving cad]
end
```

The `list`

, `option`

, and `result`

types, as well as **tuples**, are automatically mapped over, without any additional annotation or functions required. Note the set of functions generated here is restricted to those that are strictly relevant to **2d** shapes/vectors compared to the first examples that contained **3d** types.

```
module Tris : sig
type t = (V2.t * V2.t * V2.t) list
val translate : V2.t -> t -> t
val xtrans : float -> t -> t
val ytrans : float -> t -> t
val rotate : ?about:V2.t -> float -> t -> t
val zrot : ?about:V2.t -> float -> t -> t
val scale : V2.t -> t -> t
val xscale : float -> t -> t
val yscale : float -> t -> t
val mirror : V2.t -> t -> t
val affine : Affine2.t -> t -> t
end = struct
type t = (V2.t * V2.t * V2.t) list [@@deriving cad]
end
```

By default, `[@@deriving cad]`

will attempt to map over constructors other than the above basic types by using applying the `map`

function of the relevant module, or for the non-`t`

named type, using the same naming conventions as explained above.

```
module IntMap = Map.Make (Int)
type v3_map = V3.t IntMap.t [@@deriving cad]
```

Here, `IntMap.map`

will be used to apply transformations to the contained `OCADml.V3.t`

elements. The expected map function should obey the convention of the function `f`

being the first *positional* argument. If you are following the conventions of JaneStreet and/or have `base`

/`core`

open, then you may use `[@@deriving cad_jane]`

which defaults to expecting `map`

functions to accept a keyword parameter `~f`

instead. If you are deriving a record containing types with mixed mapping conventions, you can make use of the `[@cad.map]`

and `[@cad.mapf]`

attributes to specify fields that do not match your default convention.

If the constructor type is not named `t`

as in this example, then this ppx will attempt to use a function with the suffix `_map`

. For example, if the type above was instead `V3.t int_map`

, the function `int_map_map`

will be expected in the scope of the derived type.

Annotating types in module sigs and `.mli`

files will generate the relevant type signatures.

```
module PolyScads : sig
type ('s, 'r, 'a) t =
{ a : ('s, 'r, 'a) Scad.t
; b : ('s, 'r, 'a) Scad.t
}
[@@deriving cad]
end = struct
type ('s, 'r, 'a) t =
{ a : ('s, 'r, 'a) Scad.t
; b : ('s, 'r, 'a) Scad.t
}
[@@deriving cad]
end
```

Note that this is also an example of polymorphism over the dimensionality of the `OSCADml.Scad.t`

type. Of course, when the type could be either **2d** or **3d**, only **2d** transformations will be available (translation, rotation, scaling, and mirroring), as in the mappable type example.

This annotation should be applied to types and record fields which represent unit vector. Types/fields marked with this will not be subject to transformations that would cause them to lose their identity as such, or rotate about anything other than the world origin. Thus:

- translate and scale will not be applied (identity function instead)
- the
`?about`

parameter will not be passed to the rotation functions (`rotate`

,`axis_rotate`

, and`quaternion`

) applied to the type marked by`@cad.unit`

.

**For example:**

```
type plane =
{ scad : Scad.d3
; normal : V3.t [@cad.unit]
}
[@@deriving cad]
```

**In this case the following would hold:**

```
let true =
let plane = { scad = Scad.cube (v3 10. 10. 0.001); normal = v3 0. 0. 1. } in
let trans = translate_plane (v3 5. 5. 0.) plane in
V3.equal plane.normal trans.normal
```

This annotation marks a field (in a record, not applicable to abstract types) to be ignored by all generated transformations. This is useful for ignoring whatever flags/configuration data that you want to carry around along with your type for which the relevant functions have not been implemented.

```
type mark =
{ scad : Scad.d3
; centre : V3.t
; id : int [@cad.ignore]
}
[@@deriving cad]
```

This annotation marks a type/field for which the transformable type is contained within a mappable type (aka functor), for which `map`

is defined, and whose parameter convention differs from the default specified by the deriver attached to the type declaration.

`[@@deriving cad]`

-> positional`f`

expected (e.g.`map f`

)`[@@deriving cad_jane]`

-> keyword`~f`

expected (e.g.`map ~f`

)

Thus, `[@cad.map]`

indicates that relevant `map`

functions will obey the convention of `f`

being the first *positional* argument (overiding `[@@deriving cad_jane]`

), whereas `[@cad.mapf]`

indicates that a keyword argument of `~f`

is expected instead (overiding `[@@deriving cad]`

). These attributes are not required for the `list`

, `option`

, and `result`

types, as they do not rely on any functions in scope.

```
open Base
module IntMap = Caml.Map.Make (Int)
module JaneOption = Option (* aliased since option is special cased *)
module MixedMapConventions : sig
type t =
{ std : v3 IntMap.t
; jane : v3 JaneOption.t
}
[@@deriving cad]
end = struct
type t =
{ std : v3 IntMap.t
; jane : v3 JaneOption.t [@cad.mapf]
}
[@@deriving cad]
end
```

When the dimensionality of a type is ambiguous (e.g. containing no fields with concretely dimensional types such as `Scad.d3`

, or `V2.t`

), these annotations should be used to specify the correct set of functions/signatures to be generated.

```
module AmbiguousDims : sig
type 'a p =
{ a : 'a [@cad.ignore]
; v : v2
}
[@@deriving cad]
type 'a t = { p : 'a p [@cad.d2] } [@@deriving cad]
end = struct
type 'a p =
{ a : 'a [@cad.ignore]
; v : v2
}
[@@deriving cad]
type 'a t = { p : 'a p [@cad.d2] } [@@deriving cad]
end
```

Here, there are no `OCADml`

(or related) types present in `'a t`

that can clue `[@@deriving cad]`

into whether it is **2d** or **3d**, so we tag on an attribute to clear it up.