Skip to content
This repository has been archived by the owner on Jan 1, 2020. It is now read-only.

Commit

Permalink
refactor build() to simplify the duck-typing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
gre committed Jan 16, 2016
1 parent cb2bf36 commit 5b01b8a
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 78 deletions.
111 changes: 47 additions & 64 deletions src/data/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ const invariant = require("invariant");
const Uniform = require("../Uniform");
const Shaders = require("../Shaders");
const TextureObjects = require("./TextureObjects");
const isNonSamplerUniformValue = require("./isNonSamplerUniformValue");
const duckTypeUniformValue = require("./duckTypeUniformValue");
const findGLNodeInGLComponentChildren = require("./findGLNodeInGLComponentChildren");
const unifyPropsWithContext = require("./unifyPropsWithContext");
const invariantStrictPositive = require("./invariantStrictPositive");
const isAnimated = require("../isAnimated");

//// build: converts the gl-react VDOM DSL into an internal data tree.

Expand Down Expand Up @@ -49,79 +48,63 @@ module.exports = function build (GLNode, context, parentPreload, via, surfaceId,
});

Object.keys(uniforms).forEach(name => {
let value = uniforms[name];

// Following part is a bit tricky: duck-typing to support many formats

let nonSamplerUniformTyp = isNonSamplerUniformValue(value);
if (nonSamplerUniformTyp) {
if (process.env.NODE_ENV!=="production" && nonSamplerUniformTyp === "number[]") {
let i = value.length;
while (i-- > 0 && (isAnimated(value[i]) || !isNaN(value[i])));
invariant(i < 0, "Shader '%s': uniform '%s' must be an array of numbers. Found '%s' at index %s", shaderName, name, value[i], i);
}
return;
}

let opts, typ = typeof value;

if (value && typ === "object" && !value.prototype && "value" in value) {
let value = uniforms[name], opts;
if (value && typeof value === "object" && !value.prototype && "value" in value) {
// if value has a value field, we tread this field as the value, but keep opts in memory if provided
if (typeof value.opts === "object") {
opts = value.opts;
}
value = value.value;
typ = typeof value;
}

if (!value) {
// falsy value are accepted to indicate blank texture
uniforms[name] = value;
}
else if (typ === "string") {
// uri specified as a string
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts);
}
else if (typ === "object" && typeof value.uri === "string") {
// uri specified in an object, we keep all other fields for RN "local" image use-case
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts);
}
else if (typ === "object" && value.data && value.shape && value.stride) {
// ndarray kind of texture
uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts);
}
else if (isAnimated(value) ||
(value && value instanceof Array && value.length > 0 && (typeof value[0] === "number" || isAnimated(value[0])))) {
// animated cases
uniforms[name] = value;
}
else if(typ === "object" && (value instanceof Array ? React.isValidElement(value[0]) : React.isValidElement(value))) {
// value is a VDOM or array of VDOM
const res = findGLNodeInGLComponentChildren(value, newContext);
if (res) {
const { childGLNode, via } = res;
// We have found a GL.Node children, we integrate it in the tree and recursively do the same
try {
switch (duckTypeUniformValue(value)) {

children.push({
vdom: value,
uniform: name,
data: build(childGLNode, newContext, preload, via, surfaceId, decorateOnShaderCompile)
});
}
else {
// in other cases VDOM, we will use child as a content
contents.push({
vdom: value,
uniform: name,
opts
});
case "string": // uri specified as a string
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts);
break;

case "{uri}": // uri specified in an object, we keep all other fields for RN "local" image use-case
uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts);
break;

case "ndarray":
uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts);
break;

case "vdom[]":
case "vdom":
const res = findGLNodeInGLComponentChildren(value, newContext);
if (res) {
const { childGLNode, via } = res;
// We have found a GL.Node children, we integrate it in the tree and recursively do the same
children.push({
vdom: value,
uniform: name,
data: build(childGLNode, newContext, preload, via, surfaceId, decorateOnShaderCompile)
});
}
else {
// in other cases VDOM, we will use child as a content
contents.push({
vdom: value,
uniform: name,
opts
});
}
break;

default:
// Remaining cases will just set the value without further transformation
uniforms[name] = value;
}
}
else {
// in any other case, it is an unrecognized invalid format
catch (e) {
delete uniforms[name];
if (typeof console !== "undefined" && console.error) console.error("invalid uniform '"+name+"' value:", value); // eslint-disable-line no-console
invariant(false, "Shader '%s': Unrecognized format for uniform '%s'", shaderName, name);
const message = "Shader '"+shaderName+"': uniform '"+name+"' "+e.message;
if (process.env.NODE_ENV !== "production")
console.error(message, value); // eslint-disable-line no-console
throw new Error(message);
}
});

Expand Down
104 changes: 104 additions & 0 deletions src/data/duckTypeUniformValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const React = require("react");
const isAnimated = require("../isAnimated");

// infer the uniform value type and validate it (throw error if invalid)

module.exports = function (obj) {
let typ = typeof obj;

if (typ==="number") {
if(isNaN(obj) || !isFinite(obj)) throw new Error("invalid number: '"+obj+"'");
return typ;
}

if (typ==="boolean") {
return typ;
}

if (typ==="string") {
return typ;
}

if (typ==="undefined") {
return null;
}

if (typ === "object") {

if (!obj) {
return null;
}

if (typeof obj.uri === "string") {
return "{uri}";
}

if (obj.data && obj.shape && obj.stride) {
return "ndarray";
}

if (obj instanceof Array) {
const length = obj.length;
if (!length) throw new Error("array is empty");
let foundAnimated = false;
let foundVDOM = false;
let foundNumber = false;
let foundBoolean = false;
for (let i=0; i<length; i++) {
const val = obj[i];
const t = typeof val;
switch (t) {
case "object":
if (val && isAnimated(val))
foundAnimated = true;
else if (val && React.isValidElement(val))
foundVDOM = true;
else
throw new Error("at index "+i+", Unrecognized object: '"+val+"'");
break;

case "number":
if(isNaN(val) || !isFinite(val))
throw new Error("at index "+i+", invalid number: '"+val+"'");
foundNumber = true;
break;

case "boolean":
foundBoolean = true;
break;

default:
throw new Error("at index "+i+", Unrecognized object: "+val);
}
}

const foundNumberOrBooleanOrAnimated = foundNumber || foundBoolean || foundAnimated;
if (foundNumberOrBooleanOrAnimated && foundVDOM) {
throw new Error("Invalid array. Found both VDOM value and numbers/booleans/animated");
}

if (foundVDOM) {
return "vdom[]";
}
if (foundAnimated) {
return "animated[]";
}
if (foundNumber) {
return "number[]";
}
if (foundBoolean) {
return "boolean[]";
}
}

if (isAnimated(obj)) {
return "animated";
}

if (React.isValidElement(obj)) {
return "vdom";
}
}

throw new Error("Unrecognized object: "+obj);
};
14 changes: 0 additions & 14 deletions src/data/isNonSamplerUniformValue.js

This file was deleted.

0 comments on commit 5b01b8a

Please sign in to comment.