Replace empty names by nulls in vision group builders

This commit is contained in:
Alexander Nozik 2021-03-01 21:09:53 +03:00
parent 78a04728ba
commit 0e488563a9
27 changed files with 69 additions and 98 deletions

View File

@ -12,6 +12,7 @@
- JavaFX support moved to a separate module - JavaFX support moved to a separate module
- Threejs support moved to a separate module - Threejs support moved to a separate module
- \[Format breaking change!\] Stylesheets are moved into properties under `@stylesheet` key - \[Format breaking change!\] Stylesheets are moved into properties under `@stylesheet` key
- VisionGroup builder accepts `null` as name for statics instead of `""`
### Deprecated ### Deprecated

View File

@ -34,3 +34,5 @@ tasks.withType<Test> {
tasks.processJupyterApiResources { tasks.processJupyterApiResources {
libraryProducers = listOf("hep.dataforge.playground.VisionForgePlayGroundForJupyter") libraryProducers = listOf("hep.dataforge.playground.VisionForgePlayGroundForJupyter")
} }
tasks.findByName("shadowJar")?.dependsOn("processJupyterApiResources")

View File

@ -20,7 +20,6 @@ import kotlinx.html.stream.createHTML
import kotlinx.html.unsafe import kotlinx.html.unsafe
import kscience.plotly.Plot import kscience.plotly.Plot
import org.jetbrains.kotlinx.jupyter.api.HTML import org.jetbrains.kotlinx.jupyter.api.HTML
import org.jetbrains.kotlinx.jupyter.api.Notebook
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
import org.jetbrains.kotlinx.jupyter.api.libraries.* import org.jetbrains.kotlinx.jupyter.api.libraries.*
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
@ -44,11 +43,11 @@ internal class VisionForgePlayGroundForJupyter : JupyterIntegration() {
} }
script { script {
type = "text/javascript" type = "text/javascript"
unsafe { +"VisionForge.renderVisionsAt(\"$id\");" } unsafe { +"renderVisionsAt(\"$id\");" }
} }
} }
override fun Builder.onLoaded(notebook: Notebook?) { override fun Builder.onLoaded() {
resource(jsResource) resource(jsResource)
onLoaded { onLoaded {

View File

@ -2,62 +2,12 @@ package hep.dataforge.vision.examples
import hep.dataforge.misc.DFExperimental import hep.dataforge.misc.DFExperimental
import hep.dataforge.vision.VisionForge import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.gdml.GdmlShowcase.cubes
import hep.dataforge.vision.gdml.toVision import hep.dataforge.vision.gdml.toVision
import hep.dataforge.vision.html.ResourceLocation import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.html.fragment import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.invoke import hep.dataforge.vision.invoke
import hep.dataforge.vision.solid.Solids import hep.dataforge.vision.solid.Solids
import space.kscience.gdml.*
internal val cubes = Gdml {
val center = define.position("center")
structure {
val air = materials.composite("G4_AIR") {}
val tubeMaterial = materials.composite("tube") {}
val boxMaterial = materials.composite("box") {}
val segment = solids.tube("segment", 20, 5.0) {
rmin = 17
deltaphi = 60
aunit = AUnit.DEG.title
}
val worldBox = solids.box("largeBox", 200, 200, 200)
val smallBox = solids.box("smallBox", 30, 30, 30)
val segmentVolume = volume("segment", tubeMaterial, segment) {}
val circle = volume("composite", boxMaterial, smallBox) {
for (i in 0 until 6) {
physVolume(segmentVolume) {
positionref = center
rotation {
z = 60 * i
unit = AUnit.DEG.title
}
}
}
}
world = volume("world", air, worldBox) {
for (i in 0 until 3) {
for (j in 0 until 3) {
for (k in 0 until 3) {
physVolume(circle) {
position {
x = (-50 + i * 50)
y = (-50 + j * 50)
z = (-50 + k * 50)
}
rotation {
x = i * 120
y = j * 120
z = 120 * k
}
}
}
}
}
}
}
}
@DFExperimental @DFExperimental
fun main() = VisionForge(Solids) { fun main() = VisionForge(Solids) {

View File

@ -15,7 +15,7 @@ pluginManagement {
id("ru.mipt.npm.gradle.js") version toolsVersion id("ru.mipt.npm.gradle.js") version toolsVersion
id("ru.mipt.npm.gradle.publish") version toolsVersion id("ru.mipt.npm.gradle.publish") version toolsVersion
kotlin("jvm") version kotlinVersion kotlin("jvm") version kotlinVersion
kotlin("jupyter.api") version "0.8.3.218" kotlin("jupyter.api") version "0.8.3.236"
kotlin("js") version kotlinVersion kotlin("js") version kotlinVersion
kotlin("multiplatform") version kotlinVersion kotlin("multiplatform") version kotlinVersion
} }

View File

@ -39,7 +39,8 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
} }
} }
override fun set(name: Name, child: Vision?) { override fun set(name: Name?, child: Vision?) {
if(name == null) error("Static children are not allowed in VisionChange")
getOrPutChild(name).apply { getOrPutChild(name).apply {
vision = child vision = child
reset = vision == null reset = vision == null

View File

@ -4,7 +4,6 @@ import hep.dataforge.context.Context
import hep.dataforge.context.PluginManager import hep.dataforge.context.PluginManager
import hep.dataforge.misc.DFExperimental import hep.dataforge.misc.DFExperimental
@DFExperimental
public expect object VisionForge public expect object VisionForge
@DFExperimental @DFExperimental

View File

@ -58,7 +58,7 @@ public operator fun VisionGroup.iterator(): Iterator<Vision> = children.values.i
public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty() public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
public interface VisionContainerBuilder<in V : Vision> { public interface VisionContainerBuilder<in V : Vision> {
public operator fun set(name: Name, child: V?) public operator fun set(name: Name?, child: V?)
} }
/** /**
@ -79,6 +79,6 @@ public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(s
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit = public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
set(token.asName(), child) set(token.asName(), child)
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String, child: V?): Unit = set(key.toName(), child) public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit = set(key?.toName(), child)
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }

View File

@ -111,13 +111,14 @@ public open class VisionGroupBase(
* Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not * Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not
* allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed. * allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed.
*/ */
override fun set(name: Name, child: Vision?): Unit { override fun set(name: Name?, child: Vision?): Unit {
when { when {
name.isEmpty() -> { name == null -> {
if (child != null) { if (child != null) {
addStatic(child) addStatic(child)
} }
} }
name.isEmpty() -> error("Empty names are not allowed in VisionGroup::set")
name.length == 1 -> { name.length == 1 -> {
val token = name.tokens.first() val token = name.tokens.first()
attachChild(token, child) attachChild(token, child)

View File

@ -7,7 +7,7 @@ import kotlinx.browser.window
@JsExport @JsExport
@DFExperimental @DFExperimental
public actual object VisionForge{ public actual object VisionForge {
/** /**
* Render all visions in this [window] using current global state of [VisionForge] * Render all visions in this [window] using current global state of [VisionForge]
*/ */
@ -30,12 +30,30 @@ public actual object VisionForge{
} }
} }
private val visionForgeContext = Context("VisionForge"){ private val visionForgeContext = Context("VisionForge") {
plugin(VisionClient) plugin(VisionClient)
} }
@DFExperimental @DFExperimental
public actual val VisionForge.context: Context get() = visionForgeContext public actual val VisionForge.context: Context
get() = visionForgeContext
@DFExperimental @DFExperimental
public val VisionForge.visionClient: VisionClient get() = plugins.fetch(VisionClient) public val VisionForge.visionClient: VisionClient
get() = plugins.fetch(VisionClient)
/**
* Render all visions in this [window] using current global state of [VisionForge]
*/
@DFExperimental
@JsExport
public fun renderVisionsInWindow(): Unit = VisionForge.renderVisionsInWindow()
/**
* Render all visions in an element with a given [id]
*/
@DFExperimental
@JsExport
public fun renderVisionsAt(id: String): Unit = VisionForge.renderVisionsAt(id)

View File

@ -4,7 +4,6 @@ import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginFactory
import hep.dataforge.misc.DFExperimental import hep.dataforge.misc.DFExperimental
@DFExperimental
public actual object VisionForge public actual object VisionForge
@DFExperimental @DFExperimental

View File

@ -7,7 +7,7 @@ kotlin {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
api(project(":visionforge-solid")) api(project(":visionforge-solid"))
api("space.kscience:gdml:0.3.0-dev") api("space.kscience:gdml:0.3.0")
} }
} }
} }

View File

@ -3,7 +3,7 @@ package hep.dataforge.vision.gdml
import space.kscience.gdml.* import space.kscience.gdml.*
public object GdmlShowcase { public object GdmlShowcase {
public val cubes = Gdml { public val cubes: Gdml = Gdml {
val center = define.position("center") val center = define.position("center")
structure { structure {
val air = materials.composite("G4_AIR") {} val air = materials.composite("G4_AIR") {}

View File

@ -76,7 +76,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
if (proto[templateName] == null) { if (proto[templateName] == null) {
proto[templateName] = volume(root, volume) proto[templateName] = volume(root, volume)
} }
val ref = group.ref(templateName, physVolume.name ?: "").withPosition(root, physVolume) val ref = group.ref(templateName, physVolume.name).withPosition(root, physVolume)
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref) referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
return ref return ref
} }
@ -148,7 +148,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
fun SolidGroup.addSolid( fun SolidGroup.addSolid(
root: Gdml, root: Gdml,
solid: GdmlSolid, solid: GdmlSolid,
name: String = "", name: String? = null,
): Solid { ): Solid {
//context.solidAdded(solid) //context.solidAdded(solid)
val lScale = solid.lscale(settings.lUnit) val lScale = solid.lscale(settings.lUnit)
@ -253,14 +253,15 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
fun SolidGroup.addSolidWithCaching( fun SolidGroup.addSolidWithCaching(
root: Gdml, root: Gdml,
solid: GdmlSolid, solid: GdmlSolid,
name: String = solid.name, name: String?,
): Solid? { ): Solid? {
require(name != ""){"Can't use empty solid name. Use null instead."}
return when (settings.solidAction(solid)) { return when (settings.solidAction(solid)) {
GdmlTransformerSettings.Action.ADD -> { GdmlTransformerSettings.Action.ADD -> {
addSolid(root, solid, name) addSolid(root, solid, name)
} }
GdmlTransformerSettings.Action.PROTOTYPE -> { GdmlTransformerSettings.Action.PROTOTYPE -> {
proxySolid(root, this, solid, name) proxySolid(root, this, solid, name ?: solid.name)
} }
GdmlTransformerSettings.Action.REJECT -> { GdmlTransformerSettings.Action.REJECT -> {
//ignore //ignore
@ -280,7 +281,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
if (volume is GdmlVolume && volume.physVolumes.isEmpty() && volume.placement == null) { if (volume is GdmlVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
val solid = volume.solidref.resolve(root) val solid = volume.solidref.resolve(root)
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined") ?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
addSolidWithCaching(root, solid, physVolume.name ?: "")?.apply { addSolidWithCaching(root, solid, physVolume.name)?.apply {
configureSolid(root, this, volume, solid) configureSolid(root, this, volume, solid)
withPosition(root, physVolume) withPosition(root, physVolume)
} }
@ -309,7 +310,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved") ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
//TODO add divisions //TODO add divisions
set(Name.EMPTY, volume(root, volume)) set(null, volume(root, volume))
} }
private fun volume( private fun volume(
@ -320,7 +321,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) {
val solid: GdmlSolid = group.solidref.resolve(root) val solid: GdmlSolid = group.solidref.resolve(root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
addSolidWithCaching(root, solid)?.apply { addSolidWithCaching(root, solid, null)?.apply {
configureSolid(root, this, group, solid) configureSolid(root, this, group, solid)
} }
@ -381,7 +382,7 @@ public fun Gdml.toVision(block: GdmlTransformerSettings.() -> Unit = {}): SolidG
/** /**
* Append Gdml node to the group * Append Gdml node to the group
*/ */
public fun SolidGroup.gdml(gdml: Gdml, key: String = "", transformer: GdmlTransformerSettings.() -> Unit = {}) { public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformerSettings.() -> Unit = {}) {
val visual = gdml.toVision(transformer) val visual = gdml.toVision(transformer)
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
set(key, visual) set(key, visual)

View File

@ -11,7 +11,7 @@ public actual typealias Counter = AtomicInteger
public fun Gdml.Companion.readFile(file: Path): Gdml { public fun Gdml.Companion.readFile(file: Path): Gdml {
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8") val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
return format.parse(Gdml.serializer(), xmlReader) return format.decodeFromReader(Gdml.serializer(), xmlReader)
} }
public fun SolidGroup.gdml(file: Path, key: String = "", transformer: GdmlTransformerSettings.() -> Unit = {}) { public fun SolidGroup.gdml(file: Path, key: String = "", transformer: GdmlTransformerSettings.() -> Unit = {}) {

View File

@ -7,6 +7,7 @@ import space.kscience.gdml.Gdml
import space.kscience.gdml.decodeFromStream import space.kscience.gdml.decodeFromStream
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@Suppress("UNUSED_VARIABLE")
class TestConvertor { class TestConvertor {
@Test @Test

View File

@ -45,6 +45,6 @@ public inline fun VisionContainerBuilder<Solid>.box(
xSize: Number, xSize: Number,
ySize: Number, ySize: Number,
zSize: Number, zSize: Number,
name: String = "", name: String? = null,
action: Box.() -> Unit = {} action: Box.() -> Unit = {}
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(action).also { set(name, it) } ): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(action).also { set(name, it) }

View File

@ -32,7 +32,7 @@ public class Composite(
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.composite( public inline fun VisionContainerBuilder<Solid>.composite(
type: CompositeType, type: CompositeType,
name: String = "", name: String? = null,
builder: SolidGroup.() -> Unit, builder: SolidGroup.() -> Unit,
): Composite { ): Composite {
val group = SolidGroup().apply(builder) val group = SolidGroup().apply(builder)
@ -56,16 +56,16 @@ public inline fun VisionContainerBuilder<Solid>.composite(
} }
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.union(name: String = "", builder: SolidGroup.() -> Unit): Composite = public inline fun VisionContainerBuilder<Solid>.union(name: String? = null, builder: SolidGroup.() -> Unit): Composite =
composite(CompositeType.UNION, name, builder = builder) composite(CompositeType.UNION, name, builder = builder)
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.subtract(name: String = "", builder: SolidGroup.() -> Unit): Composite = public inline fun VisionContainerBuilder<Solid>.subtract(name: String? = null, builder: SolidGroup.() -> Unit): Composite =
composite(CompositeType.SUBTRACT, name, builder = builder) composite(CompositeType.SUBTRACT, name, builder = builder)
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.intersect( public inline fun VisionContainerBuilder<Solid>.intersect(
name: String = "", name: String? = null,
builder: SolidGroup.() -> Unit, builder: SolidGroup.() -> Unit,
): Composite = ): Composite =
composite(CompositeType.INTERSECT, name, builder = builder) composite(CompositeType.INTERSECT, name, builder = builder)

View File

@ -67,7 +67,7 @@ public class ConeSegment(
public inline fun VisionContainerBuilder<Solid>.cylinder( public inline fun VisionContainerBuilder<Solid>.cylinder(
r: Number, r: Number,
height: Number, height: Number,
name: String = "", name: String? = null,
block: ConeSegment.() -> Unit = {} block: ConeSegment.() -> Unit = {}
): ConeSegment = ConeSegment( ): ConeSegment = ConeSegment(
r.toFloat(), r.toFloat(),
@ -80,7 +80,7 @@ public inline fun VisionContainerBuilder<Solid>.cone(
bottomRadius: Number, bottomRadius: Number,
height: Number, height: Number,
upperRadius: Number = 0.0, upperRadius: Number = 0.0,
name: String = "", name: String? = null,
block: ConeSegment.() -> Unit = {} block: ConeSegment.() -> Unit = {}
): ConeSegment = ConeSegment( ): ConeSegment = ConeSegment(
bottomRadius.toFloat(), bottomRadius.toFloat(),

View File

@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable
@SerialName("solid.convex") @SerialName("solid.convex")
public class Convex(public val points: List<Point3D>) : SolidBase(), Solid public class Convex(public val points: List<Point3D>) : SolidBase(), Solid
public inline fun VisionContainerBuilder<Solid>.convex(name: String = "", action: ConvexBuilder.() -> Unit = {}): Convex = public inline fun VisionContainerBuilder<Solid>.convex(name: String? = null, action: ConvexBuilder.() -> Unit = {}): Convex =
ConvexBuilder().apply(action).build().also { set(name, it) } ConvexBuilder().apply(action).build().also { set(name, it) }
public class ConvexBuilder { public class ConvexBuilder {

View File

@ -104,5 +104,5 @@ public class Extruded(
} }
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Solid>.extrude(name: String = "", action: Extruded.() -> Unit = {}): Extruded = public fun VisionContainerBuilder<Solid>.extrude(name: String? = null, action: Extruded.() -> Unit = {}): Extruded =
Extruded().apply(action).also { set(name, it) } Extruded().apply(action).also { set(name, it) }

View File

@ -16,7 +16,8 @@ import kotlinx.serialization.Serializable
public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid { public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid {
//var lineType by string() //var lineType by string()
public var thickness: Number by allProperties(inherit = false).number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) public var thickness: Number by allProperties(inherit = false).number(1.0,
key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
public companion object { public companion object {
public val THICKNESS_KEY: Name = "thickness".asName() public val THICKNESS_KEY: Name = "thickness".asName()
@ -27,7 +28,6 @@ public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid {
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Solid>.polyline( public fun VisionContainerBuilder<Solid>.polyline(
vararg points: Point3D, vararg points: Point3D,
name: String = "", name: String? = null,
action: PolyLine.() -> Unit = {}, action: PolyLine.() -> Unit = {},
): PolyLine = ): PolyLine = PolyLine(points.toList()).apply(action).also { set(name, it) }
PolyLine(points.toList()).apply(action).also { set(name, it) }

View File

@ -83,10 +83,9 @@ public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Vision>.group( public fun VisionContainerBuilder<Vision>.group(
name: Name = Name.EMPTY, name: Name? = null,
action: SolidGroup.() -> Unit = {}, action: SolidGroup.() -> Unit = {},
): SolidGroup = ): SolidGroup = SolidGroup().apply(action).also { set(name, it) }
SolidGroup().apply(action).also { set(name, it) }
/** /**
* Define a group with given [name], attach it to this parent and return it. * Define a group with given [name], attach it to this parent and return it.

View File

@ -19,6 +19,6 @@ public fun VisionContainerBuilder<Solid>.label(
text: String, text: String,
fontSize: Number = 20, fontSize: Number = 20,
fontFamily: String = "Arial", fontFamily: String = "Arial",
name: String = "", name: String? = null,
action: SolidLabel.() -> Unit = {}, action: SolidLabel.() -> Unit = {},
): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) } ): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) }

View File

@ -170,7 +170,7 @@ public val Vision.prototype: Vision
*/ */
public fun SolidGroup.ref( public fun SolidGroup.ref(
templateName: Name, templateName: Name,
name: String = "", name: String? = null,
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) } ): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) }
/** /**

View File

@ -56,7 +56,7 @@ public inline fun VisionContainerBuilder<Solid>.sphere(
radius: Number, radius: Number,
phi: Number = 2 * PI, phi: Number = 2 * PI,
theta: Number = PI, theta: Number = PI,
name: String = "", name: String? = null,
action: Sphere.() -> Unit = {}, action: Sphere.() -> Unit = {},
): Sphere = Sphere( ): Sphere = Sphere(
radius.toFloat(), radius.toFloat(),

View File

@ -126,7 +126,7 @@ public inline fun VisionContainerBuilder<Solid>.tube(
innerRadius: Number = 0f, innerRadius: Number = 0f,
startAngle: Number = 0f, startAngle: Number = 0f,
angle: Number = 2 * PI, angle: Number = 2 * PI,
name: String = "", name: String? = null,
block: Tube.() -> Unit = {}, block: Tube.() -> Unit = {},
): Tube = Tube( ): Tube = Tube(
r.toFloat(), r.toFloat(),