Home > database >  Method that accepts Function or Float for any parameter
Method that accepts Function or Float for any parameter

Time:11-03

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.

  • Related