I am basically trying to return a Function, used for a Stream, where any parameter can be either a Float or a Function. I was looking to make the input of dynamic values simpler for creative coding applications and feel I'm in over my head with streams and generics.
Is there a more elegant way of doing this? Overloading the method would create a lot of duplicate code since I want any parameter to be either type. Also I looked at trying to create an interface, but couldn't do it without creating new class instances just to input a float as a parameter.
class A{
Function<PVector, Float> range( Object scale, Object min, Object max ){
FObj<PVector> fScale = getFO( scale );
FObj<PVector> fMin = getFO( min );
FObj<PVector> fMax = getFO( max );
return pv -> map(noise(pv.x * fScale.getValue( pv ), pv.y * fScale.getValue( pv ) ), 0, 1, fMin.getValue( pv ), fMax.getValue( pv ) );
}
<T> FObj<T> getFO ( Object input ) {
if( input instanceof Float ) return new FObj<T>( (Float) input );
else if( input instanceof Function ) return new FObj<T>( (Function<T,Float>)input );
else throw new java.lang.RuntimeException("error");
}
}
class FObj<T>{
Function<T, Float> fn;
float val;
FObj( Function<T, Float> fn ){
this.fn = fn;
}
FObj( float val ){
this.val = val;
}
float getValue( T input ){
return fn != null ? fn.apply( input ) : val;
}
}
Two use cases for range method at s and s2:
A a;
List<PVector> pvs = new ArrayList<>();
pvs.add( new PVector( 0, 0 ) );
Stream s = pvs.stream().map( a.range( 0.02f, 0f, 255f )::apply );
Stream s2 = pvs.stream().map( a.range( a.range( 0.02f, 0.1f, 0.002f ), 0f, 255f )::apply );
CodePudding user response:
You can rewrite the class FObj<T>
like this:
class FObj<T> {
Function<T, Float> fn;
FObj(Function<T, Float> fn) {
this.fn = fn;
}
FObj(float val) {
this.fn = t -> val;
}
float getValue(T input) {
return fn.apply(input);
}
}
But this is almost equivalent to Function<T, Float>
, so you don't need it.
class A {
Function<PVector, Float> range(
Function<PVector, Float> fScale,
Function<PVector, Float> fMin,
Function<PVector, Float> fMax) {
return pv -> map(noise(pv.x * fScale.apply(pv), pv.y * fScale.apply(pv)),
0, 1, fMin.apply(pv), fMax.apply(pv));
}
}
and
A a = new A();
List<PVector> pvs = new ArrayList<>();
pvs.add( new PVector( 0, 0 ) );
Stream<Float> s = pvs.stream().map(
a.range( p -> 0.02f, p -> 0f, p -> 255f )::apply );
Stream<Float> s2 = pvs.stream().map(
a.range(a.range( p -> 0.02f, p -> 0.1f, p -> 0.002f ),
p -> 0f, p -> 255f )::apply );
CodePudding user response:
You should use Builder pattern.
public static void main(String... args) throws IOException {
List<PVector> pvs = List.of(new PVector(0, 0));
Stream<Double> s = pvs.stream().map(new RangeBuilder().scale(.02)
.min(0)
.max(255)
.build());
Stream<Double> s2 = pvs.stream().map(new RangeBuilder().scale(new RangeBuilder().scale(.02)
.min(.1)
.max(.002)
.build())
.min(0)
.max(255)
.build());
}
public static final class RangeBuilder {
private Function<PVector, Double> scale;
private Function<PVector, Double> min;
private Function<PVector, Double> max;
public RangeBuilder scale(Function<PVector, Double> scale) {
this.scale = scale;
return this;
}
public RangeBuilder scale(double scale) {
return scale(in -> scale);
}
public RangeBuilder min(Function<PVector, Double> min) {
this.min = min;
return this;
}
public RangeBuilder min(double min) {
return min(in -> min);
}
public RangeBuilder max(Function<PVector, Double> max) {
this.max = max;
return this;
}
public RangeBuilder max(double scale) {
return scale(in -> scale);
}
public Function<PVector, Double> build() {
return pv -> map(noise(pv.x * scale.apply(pv), pv.y * scale.apply(pv)), 0, 1, min.apply(pv), max.apply(pv));
}
}
P.S. I user double
instead of float
, because this is default type for floating point numbers.