v0.2.0-dev-22 #47
@ -19,59 +19,66 @@ import kotlin.random.Random
|
|||||||
private val solidsName = "solids".asName()
|
private val solidsName = "solids".asName()
|
||||||
private val volumesName = "volumes".asName()
|
private val volumesName = "volumes".asName()
|
||||||
|
|
||||||
public class GDMLTransformer internal constructor(public val root: GDML) {
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
//private val materialCache = HashMap<GDMLMaterial, Meta>()
|
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||||
private val random = Random(222)
|
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
|
public class GDMLTransformerSettings {
|
||||||
public enum class Action {
|
public enum class Action {
|
||||||
ADD,
|
ADD,
|
||||||
REJECT,
|
REJECT,
|
||||||
PROTOTYPE
|
PROTOTYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lUnit: LUnit = LUnit.MM
|
public var lUnit: LUnit = LUnit.CM
|
||||||
public var aUnit: AUnit = AUnit.RADIAN
|
public var aUnit: AUnit = AUnit.RADIAN
|
||||||
|
|
||||||
public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE }
|
public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE }
|
||||||
public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE }
|
public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GDMLTransformer(val settings: GDMLTransformerSettings) {
|
||||||
|
//private val materialCache = HashMap<GDMLMaterial, Meta>()
|
||||||
|
private val random = Random(222)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special group for local templates
|
* A special group for local templates
|
||||||
*/
|
*/
|
||||||
private val proto by lazy { SolidGroup() }
|
private val proto = SolidGroup()
|
||||||
|
|
||||||
private val solids by lazy {
|
private val solids = proto.group(solidsName) {
|
||||||
proto.group(solidsName) {
|
|
||||||
config["edges.enabled"] = false
|
config["edges.enabled"] = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private val referenceStore = HashMap<Name, MutableList<Proxy>>()
|
private val referenceStore = HashMap<Name, MutableList<Proxy>>()
|
||||||
|
|
||||||
private fun proxySolid(group: SolidGroup, solid: GDMLSolid, name: String): Proxy {
|
private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): Proxy {
|
||||||
val templateName = solidsName + name
|
val templateName = solidsName + name
|
||||||
if (proto[templateName] == null) {
|
if (proto[templateName] == null) {
|
||||||
solids.addSolid(solid, name)
|
solids.addSolid(root, solid, name)
|
||||||
}
|
}
|
||||||
val ref = group.ref(templateName, name)
|
val ref = group.ref(templateName, name)
|
||||||
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
|
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun proxyVolume(group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): Proxy {
|
private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): Proxy {
|
||||||
val templateName = volumesName + volume.name.asName()
|
val templateName = volumesName + volume.name.asName()
|
||||||
if (proto[templateName] == null) {
|
if (proto[templateName] == null) {
|
||||||
proto[templateName] = volume(volume)
|
proto[templateName] = volume(root, volume)
|
||||||
}
|
}
|
||||||
val ref = group.ref(templateName, physVolume.name ?: "").withPosition(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
|
||||||
}
|
}
|
||||||
|
|
||||||
private val styleCache = HashMap<Name, Meta>()
|
private val styleCache = HashMap<Name, Meta>()
|
||||||
|
|
||||||
public var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ ->
|
var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ ->
|
||||||
lUnit = LUnit.CM
|
|
||||||
if (parent.physVolumes.isNotEmpty()) {
|
if (parent.physVolumes.isNotEmpty()) {
|
||||||
useStyle("opaque") {
|
useStyle("opaque") {
|
||||||
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
||||||
@ -80,14 +87,14 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
||||||
styleCache.getOrPut(name.toName()) {
|
styleCache.getOrPut(name.toName()) {
|
||||||
Meta(builder)
|
Meta(builder)
|
||||||
}
|
}
|
||||||
useStyle(name)
|
useStyle(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureSolid(obj: Solid, parent: GDMLVolume, solid: GDMLSolid) {
|
fun configureSolid(root: GDML, obj: Solid, parent: GDMLVolume, solid: GDMLSolid) {
|
||||||
val material = parent.materialref.resolve(root) ?: GDMLElement(parent.materialref.ref)
|
val material = parent.materialref.resolve(root) ?: GDMLElement(parent.materialref.ref)
|
||||||
|
|
||||||
val styleName = "material[${material.name}]"
|
val styleName = "material[${material.name}]"
|
||||||
@ -100,22 +107,19 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
obj.solidConfiguration(parent, solid)
|
obj.solidConfiguration(parent, solid)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var onFinish: GDMLTransformer.() -> Unit = {}
|
fun <T : Solid> T.withPosition(
|
||||||
|
|
||||||
|
|
||||||
private fun <T : Solid> T.withPosition(
|
|
||||||
newPos: GDMLPosition? = null,
|
newPos: GDMLPosition? = null,
|
||||||
newRotation: GDMLRotation? = null,
|
newRotation: GDMLRotation? = null,
|
||||||
newScale: GDMLScale? = null
|
newScale: GDMLScale? = null,
|
||||||
): T = apply {
|
): T = apply {
|
||||||
newPos?.let {
|
newPos?.let {
|
||||||
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit))
|
val point = Point3D(it.x(settings.lUnit), it.y(settings.lUnit), it.z(settings.lUnit))
|
||||||
if (position != null || point != World.ZERO) {
|
if (position != null || point != World.ZERO) {
|
||||||
position = point
|
position = point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newRotation?.let {
|
newRotation?.let {
|
||||||
val point = Point3D(it.x(aUnit), it.y(aUnit), it.z(aUnit))
|
val point = Point3D(it.x(settings.aUnit), it.y(settings.aUnit), it.z(settings.aUnit))
|
||||||
if (rotation != null || point != World.ZERO) {
|
if (rotation != null || point != World.ZERO) {
|
||||||
rotation = point
|
rotation = point
|
||||||
}
|
}
|
||||||
@ -130,24 +134,19 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
//TODO convert units if needed
|
//TODO convert units if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Solid> T.withPosition(physVolume: GDMLPhysVolume): T = withPosition(
|
fun <T : Solid> T.withPosition(root: GDML, physVolume: GDMLPhysVolume): T = withPosition(
|
||||||
physVolume.resolvePosition(root),
|
physVolume.resolvePosition(root),
|
||||||
physVolume.resolveRotation(root),
|
physVolume.resolveRotation(root),
|
||||||
physVolume.resolveScale(root)
|
physVolume.resolveScale(root)
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
fun SolidGroup.addSolid(
|
||||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
root: GDML,
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
|
||||||
|
|
||||||
private fun SolidGroup.addSolid(
|
|
||||||
solid: GDMLSolid,
|
solid: GDMLSolid,
|
||||||
name: String = ""
|
name: String = "",
|
||||||
): Solid {
|
): Solid {
|
||||||
//context.solidAdded(solid)
|
//context.solidAdded(solid)
|
||||||
val lScale = solid.lscale(lUnit)
|
val lScale = solid.lscale(settings.lUnit)
|
||||||
val aScale = solid.ascale()
|
val aScale = solid.ascale()
|
||||||
return when (solid) {
|
return when (solid) {
|
||||||
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
||||||
@ -185,7 +184,7 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
val innerSolid: GDMLSolid = solid.solidref.resolve(root)
|
val innerSolid: GDMLSolid = solid.solidref.resolve(root)
|
||||||
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
||||||
|
|
||||||
addSolid(innerSolid, name).apply {
|
addSolid(root, innerSolid, name).apply {
|
||||||
scaleX *= solid.scale.x.toFloat()
|
scaleX *= solid.scale.x.toFloat()
|
||||||
scaleY *= solid.scale.y.toFloat()
|
scaleY *= solid.scale.y.toFloat()
|
||||||
scaleZ = solid.scale.z.toFloat()
|
scaleZ = solid.scale.z.toFloat()
|
||||||
@ -221,13 +220,13 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return composite(type, name) {
|
return composite(type, name) {
|
||||||
addSolid(first).withPosition(
|
addSolid(root, first).withPosition(
|
||||||
solid.resolveFirstPosition(root),
|
solid.resolveFirstPosition(root),
|
||||||
solid.resolveFirstRotation(root),
|
solid.resolveFirstRotation(root),
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
addSolid(second).withPosition(
|
addSolid(root, second).withPosition(
|
||||||
solid.resolvePosition(root),
|
solid.resolvePosition(root),
|
||||||
solid.resolveRotation(root),
|
solid.resolveRotation(root),
|
||||||
null
|
null
|
||||||
@ -239,26 +238,28 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SolidGroup.addSolidWithCaching(
|
fun SolidGroup.addSolidWithCaching(
|
||||||
|
root: GDML,
|
||||||
solid: GDMLSolid,
|
solid: GDMLSolid,
|
||||||
name: String = solid.name
|
name: String = solid.name,
|
||||||
): Solid? {
|
): Solid? {
|
||||||
return when (solidAction(solid)) {
|
return when (settings.solidAction(solid)) {
|
||||||
Action.ADD -> {
|
GDMLTransformerSettings.Action.ADD -> {
|
||||||
addSolid(solid, name)
|
addSolid(root, solid, name)
|
||||||
}
|
}
|
||||||
Action.PROTOTYPE -> {
|
GDMLTransformerSettings.Action.PROTOTYPE -> {
|
||||||
proxySolid(this, solid, name)
|
proxySolid(root, this, solid, name)
|
||||||
}
|
}
|
||||||
Action.REJECT -> {
|
GDMLTransformerSettings.Action.REJECT -> {
|
||||||
//ignore
|
//ignore
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SolidGroup.addPhysicalVolume(
|
fun SolidGroup.addPhysicalVolume(
|
||||||
physVolume: GDMLPhysVolume
|
root: GDML,
|
||||||
|
physVolume: GDMLPhysVolume,
|
||||||
) {
|
) {
|
||||||
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
||||||
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
||||||
@ -267,60 +268,62 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
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(solid, physVolume.name ?: "")?.apply {
|
addSolidWithCaching(root, solid, physVolume.name ?: "")?.apply {
|
||||||
configureSolid(this, volume, solid)
|
configureSolid(root, this, volume, solid)
|
||||||
withPosition(physVolume)
|
withPosition(root, physVolume)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
when (volumeAction(volume)) {
|
when (settings.volumeAction(volume)) {
|
||||||
Action.ADD -> {
|
GDMLTransformerSettings.Action.ADD -> {
|
||||||
val group: SolidGroup = volume(volume)
|
val group: SolidGroup = volume(root, volume)
|
||||||
this[physVolume.name ?: ""] = group.withPosition(physVolume)
|
this[physVolume.name ?: ""] = group.withPosition(root, physVolume)
|
||||||
}
|
}
|
||||||
Action.PROTOTYPE -> {
|
GDMLTransformerSettings.Action.PROTOTYPE -> {
|
||||||
proxyVolume(this, physVolume, volume)
|
proxyVolume(root, this, physVolume, volume)
|
||||||
}
|
}
|
||||||
Action.REJECT -> {
|
GDMLTransformerSettings.Action.REJECT -> {
|
||||||
//ignore
|
//ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SolidGroup.addDivisionVolume(
|
fun SolidGroup.addDivisionVolume(
|
||||||
divisionVolume: GDMLDivisionVolume
|
root: GDML,
|
||||||
|
divisionVolume: GDMLDivisionVolume,
|
||||||
) {
|
) {
|
||||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
|
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
|
||||||
?: 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(volume))
|
set(Name.EMPTY, volume(root, volume))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun volume(
|
private fun volume(
|
||||||
group: GDMLGroup
|
root: GDML,
|
||||||
|
group: GDMLGroup,
|
||||||
): SolidGroup = SolidGroup().apply {
|
): SolidGroup = SolidGroup().apply {
|
||||||
if (group is GDMLVolume) {
|
if (group is GDMLVolume) {
|
||||||
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(solid)?.apply {
|
addSolidWithCaching(root, solid)?.apply {
|
||||||
configureSolid(this, group, solid)
|
configureSolid(root, this, group, solid)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val vol: GDMLPlacement? = group.placement) {
|
when (val vol: GDMLPlacement? = group.placement) {
|
||||||
is GDMLPhysVolume -> addPhysicalVolume(vol)
|
is GDMLPhysVolume -> addPhysicalVolume(root, vol)
|
||||||
is GDMLDivisionVolume -> addDivisionVolume(vol)
|
is GDMLDivisionVolume -> addDivisionVolume(root, vol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.physVolumes.forEach { physVolume ->
|
group.physVolumes.forEach { physVolume ->
|
||||||
addPhysicalVolume(physVolume)
|
addPhysicalVolume(root, physVolume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun finalize(final: SolidGroup): SolidGroup {
|
fun finalize(final: SolidGroup): SolidGroup {
|
||||||
//final.prototypes = proto
|
//final.prototypes = proto
|
||||||
final.useStyle("GDML") {
|
final.useStyle("GDML") {
|
||||||
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
|
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
|
||||||
@ -351,25 +354,22 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
|
|||||||
define(it.key.toString(), it.value)
|
define(it.key.toString(), it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onFinish(this@GDMLTransformer)
|
|
||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
public val result: SolidGroup by lazy {
|
fun transform(root: GDML): SolidGroup = finalize(volume(root, root.world))
|
||||||
finalize(volume(root.world))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup {
|
public fun GDML.toVision(block: GDMLTransformerSettings.() -> Unit = {}): SolidGroup {
|
||||||
val context = GDMLTransformer(this).apply(block)
|
val context = GDMLTransformer(GDMLTransformerSettings().apply(block))
|
||||||
return context.result
|
return context.transform(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append gdml node to the group
|
* Append gdml node to the group
|
||||||
*/
|
*/
|
||||||
public fun SolidGroup.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
public fun SolidGroup.gdml(gdml: GDML, key: String = "", 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)
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
package hep.dataforge.vision.gdml
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
import hep.dataforge.vision.solid.SolidGroup
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
import nl.adaptivity.xmlutil.StAXReader
|
|
||||||
import kscience.gdml.GDML
|
import kscience.gdml.GDML
|
||||||
|
import nl.adaptivity.xmlutil.StAXReader
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
public actual typealias Counter = AtomicInteger
|
public actual typealias Counter = AtomicInteger
|
||||||
|
|
||||||
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.parse(GDML.serializer(), xmlReader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SolidGroup.gdml(file: Path, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
public fun SolidGroup.gdml(file: Path, key: String = "", transformer: GDMLTransformerSettings.() -> Unit = {}) {
|
||||||
val gdml = GDML.readFile(file)
|
val gdml = GDML.readFile(file)
|
||||||
gdml(gdml, key, transformer)
|
gdml(gdml, key, transformer)
|
||||||
}
|
}
|
||||||
|
@ -50,14 +50,10 @@ public abstract class AbstractProxy : BasicSolid(), VisionGroup {
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid.proxy")
|
@SerialName("solid.proxy")
|
||||||
public class Proxy private constructor(
|
public class Proxy(
|
||||||
public val templateName: Name,
|
public val templateName: Name,
|
||||||
) : AbstractProxy(), Solid {
|
) : AbstractProxy(), Solid {
|
||||||
|
|
||||||
public constructor(parent: SolidGroup, templateName: Name) : this(templateName) {
|
|
||||||
this.parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively search for defined template in the parent
|
* Recursively search for defined template in the parent
|
||||||
*/
|
*/
|
||||||
@ -145,7 +141,7 @@ public val Vision.prototype: Vision
|
|||||||
public fun SolidGroup.ref(
|
public fun SolidGroup.ref(
|
||||||
templateName: Name,
|
templateName: Name,
|
||||||
name: String = "",
|
name: String = "",
|
||||||
): Proxy = Proxy(this, templateName).also { set(name, it) }
|
): Proxy = Proxy(templateName).also { set(name, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add new proxy wrapping given object and automatically adding it to the prototypes
|
* Add new proxy wrapping given object and automatically adding it to the prototypes
|
||||||
|
Loading…
Reference in New Issue
Block a user