AKTIVE

sdf.tcl at trunk
Login

sdf.tcl at trunk

File etc/generator/virtual/sdf.tcl artifact 3563bc5249 on branch trunk


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132
   133
   134
   135
   136
   137
   138
   139
   140
   141
   142
   143
   144
   145
   146
   147
   148
   149
   150
   151
   152
   153
   154
   155
   156
   157
   158
   159
   160
   161
   162
   163
   164
   165
   166
   167
   168
   169
   170
   171
   172
   173
   174
   175
   176
   177
   178
   179
   180
   181
   182
   183
   184
   185
   186
   187
   188
   189
   190
   191
   192
   193
   194
   195
   196
   197
   198
   199
   200
   201
   202
   203
   204
   205
   206
   207
   208
   209
   210
   211
   212
   213
   214
   215
   216
   217
   218
   219
   220
   221
   222
   223
   224
   225
   226
   227
   228
   229
   230
   231
   232
   233
   234
   235
   236
   237
   238
   239
   240
   241
   242
   243
   244
   245
   246
   247
   248
   249
   250
   251
   252
   253
   254
   255
   256
   257
   258
   259
   260
   261
   262
   263
   264
   265
   266
   267
   268
   269
   270
   271
   272
   273
   274
   275
   276
   277
   278
   279
   280
   281
   282
   283
   284
   285
   286
   287
   288
   289
   290
   291
   292
   293
   294
   295
   296
   297
   298
   299
   300
   301
   302
   303
   304
   305
   306
   307
   308
   309
   310
   311
   312
   313
   314
   315
   316
   317
   318
   319
   320
   321
   322
   323
   324
   325
   326
   327
   328
   329
   330
   331
   332
   333
   334
   335
   336
   337
   338
   339
   340
   341
   342
   343
   344
   345
   346
   347
   348
   349
   350
   351
   352
   353
   354
   355
   356
   357
   358
   359
   360
   361
   362
   363
   364
   365
   366
   367
   368
   369
   370
   371
   372
   373
   374
   375
   376
   377
   378
   379
   380
   381
   382
## -*- mode: tcl ; fill-column: 90 -*-
# # ## ### ##### ######## ############# #####################
## Generators -- signed distance fields

#
# SDF manipulations .............................
#
# Example elements:
#
# 1. set box    [aktive image sdf box-rounded width 1000 height 1000 ewidth 400 eheight 200 center {500 500} downrightradius 100]
# 2. set circle [aktive image sdf circle width 1000 height 1000 radius 200 center {500 300}]
#
# Singular manipulations ........................
#
#  <> is the place holder for the operation converting the SDF into a regular image (not `fit`).
#
# (a) image = (1).<>                          -> filled box
# (b)       = (1).abs.<>                      -> outlined box
# (c)       = (1).ring(15).<>                 -> thick outlined box
# (d)       = (1).ring(15).abs.<>             -> double outlined box
# (e)       = (1).abs.ring(15).<>             -> See (c), abs has no effect
# (f)       = (1).ring(15).abs.ring(2).<>     -> double thick outlined box
# (g)       = (1).ring(15).abs.ring(2).abs.<> -> quadruple outlined box
#
# Combining SDFs ................................
#
#   Basic combiners:
#
#     min                   -> or, union
#     max                   -> and, intersection
#     neg                   -> not, inversion
#
#     and (a, not b)        -> sub/traction
#     sub (a or b, a and b) -> xor, symmetric difference
#
#   abs  or not before combining, per element
#   ring or not before combining, per element
#
#   abs         after combining -- See above, singular manipulations
#   ring        after combining -- See above, singular manipulations
#
#     (1) (2) combiner
# -------+---+----
# (a)         min -> union  filled   circle, filled   box
# (b) abs     min -> union  filled   circle, outlined box
# (c)     abs min -> union  outlined circle, filled   box
# (d) abs abs min -> union  outlined circle, outlined box
#
# (e)         max -> isect  filled   circle, filled   box
# (f) abs     max -> isect  filled   circle, outlined box (box outline in circle)
# (g)     abs max -> isect  outlined circle, filled   box (circle outline in box)
# (h) abs abs max -> isect  outlined circle, outlined box (points, circle/box crossings)
#
# (i)         add -> (*)
# (j) abs     add -> (*)
# (k)     abs add -> (*)
# (l) abs abs add -> See (h)
#
# (m)         sub -> (*)
# (n) abs     sub -> (*)
# (o)     abs sub -> (*)
# (p) abs abs sub -> (*)
#
# (*) Weird gradient? shapes
#
# Note that SDF make interesting images of their own when fit/compressed into the [0..1]
# range. Terrain pseudo 3D display through shading.
#
# --> operations for and, or, not, xor, outlining, rounding, ...
#

# # ## ### ##### ######## ############# #####################
## SDF primitives

operator [sdf-known image::sdf::] {
    section generator virtual sdf

    op -> _ mode sdf

    def element [sdf-label $sdf]

    note Returns an image with the given dimensions and location, \
	containing the signed distance field of a $element.

    ref https://iquilezles.org/articles/distfunctions2d

    import sdf/note.tcl

    note See also "<!xref: aktive op draw $sdf on>" \
	and "<!xref: aktive image draw ${sdf}>."

    sdf-common-params

    import sdf/parameter/$sdf.tcl
    import sdf/coding/$sdf.tcl
}

# # ## ### ##### ######## ############# #####################
## Supporting operators I -- boolean combiners/modifiers

operator op::sdf::not {
    section transform sdf

    # !xref-mark sdf
    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	@1 | ; sdf-fit ; sdf-smooth ; sdf-pixelated
    }
    # !xref-mark /end

    note Returns the inverted input SDF, where inside and outside changed places. \
	This is defined as `1 - SRC`.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    body {
	aktive op math1 neg $src
    }
}

operator op::sdf::or {
    section transform sdf combiner

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	@1 @2 | sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Returns the union (`+`, `min`) of all input SDFs.

    ref https://iquilezles.org/articles/distfunctions2d

    input...

    body {
	aktive op math min {*}$args
    }
}

operator op::sdf::and {
    section transform sdf combiner

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	@1 @2 | sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Returns the intersection (`*`, `max`) of all input SDFs.

    ref https://iquilezles.org/articles/distfunctions2d

    input...

    body {
	aktive op math max {*}$args
    }
}

operator op::sdf::sub {
    section transform sdf combiner

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	@1 @2 | sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Returns the difference `A - B` of the two input SDFs. \
	This is defined as `A * (not B)`.

    ref https://iquilezles.org/articles/distfunctions2d

    input a	SDF A
    input b	SDF B

    body {
	# The difference `A-B is the intersection of `A` and `not B`
	and $a [not $b]
    }
}

operator op::sdf::xor {
    section transform sdf combiner

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	@1 @2 | sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Returns the symmetric difference of all input SDFs.

    note A single input is passed through unchanged.

    ref https://iquilezles.org/articles/distfunctions2d

    input...

    body {
	aktive::aggregate-or-pass {
	    aktive op sdf xor-core
	} $args
    }
}

operator op::sdf::xor-core {
    section transform sdf combiner

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	@1 @2 | sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Returns the symmetric difference of the two input SDFs. \
	This is defined as `(A + B) - (A * B)`.

    ref https://iquilezles.org/articles/distfunctions2d

    input a	SDF A
    input b	SDF B

    body {
	# The symmetric difference is the union minus the intersection
	sub [or $a $b] [and $a $b]
    }
}

# # ## ### ##### ######## ############# #####################
## Supporting operators II -- appearance modulation

operator op::sdf::ring {
    section transform sdf

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	@1 thickness 4 | ; sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Combines outlining and rounding to replace the input SDF with \
	an SDF tracing the border at some thickness and returns the result.

    note The result is annular, i.e. has a ring/onion-like structure.

    note Note that a thickness of zero devolves this operation to a plain \
	outline.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    double thickness Desired border thickness.

    body {
	round [outline $src] radius $thickness
    }
}

operator op::sdf::outline {
    section transform sdf

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	@1 | ; sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Replaces the input SDF with an outlined form, and returns the result.

    note This is implemented by taking the absolute of the input.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    body {
	aktive op math1 abs $src
    }
}

operator op::sdf::round {
    section transform sdf

    example {
	aktive image sdf box center {64 64} width 128 height 128 ewidth 40 eheight 40
	@1 radius 20 | ; sdf-fit ; sdf-smooth ; sdf-pixelated
    }

    note Replaces the input SDF with a more rounded form per the radius, \
	and returns the result.

    note This is implemented by shifting the input SDF down by the radius.

    note For a radius > 0 this expands the SDF, making the encoded element \
	rounder. A radius < 0 conversely shrinks the SDF.

    note To get a rounded SDF at the original size use a pre-shrunken/expanded \
	SDF as the input to compensate the changes made by this operator.

    note A radius of zero is ignored.

    ref https://iquilezles.org/articles/distfunctions2d

    input
    double radius Expansion/Shrinkage radius for the SDF.

    body {
	if {$radius == 0} { return $src }
	aktive op math1 shift $src offset [expr {- $radius}]
    }
}

# # ## ### ##### ######## ############# #####################
## Supporting operators III -- Conversions into image

operator op::sdf::2image::fit {
    section transform sdf

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	aktive op sdf and @1 @2 | sdf-fit
    }

    note Compresses the input SDF into the range 0..1 and returns the resulting grayscale image.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    body {
	aktive op math1 fit min-max $src
    }
}

operator op::sdf::2image::smooth {
    section transform sdf

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	aktive op sdf and @1 @2 | sdf-smooth
    }

    note Converts the SDF into a grey-scale image with anti-aliased element borders.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    body {
	set src [aktive op math1 clamp $src]
	set src [aktive op math1 invert $src]
    }
}

operator op::sdf::2image::pixelated {
    section transform sdf

    example {
	aktive image sdf circle center {80 80} width 128 height 128 radius 40
	aktive image sdf box    center {48 48} width 128 height 128 ewidth 40 eheight 40
	aktive op sdf and @1 @2 | sdf-pixelated
    }

    note Converts the SDF into a black/white image with pixelated element borders.

    ref https://iquilezles.org/articles/distfunctions2d

    input

    body {
	aktive op math1 lt $src threshold 0.5
    }
}

##
# # ## ### ##### ######## ############# #####################
::return