How can I get a transparent arrow without the shaft showing through the head?

edited February 2018 in Questions about Code

My code

 colorMode(HSB, 6.0, 1.0, 1.0, 1.0);
 background(0); 
 size(500, 500);
 strokeWeight(1);
 stroke(1,1,1,0.5);
 fill(1,1,1,0.5);
 translate(100,100);
 line(0,0,0,-50);
 noStroke();
 triangle(0.5 /*partial workaround for triangle asymmetry*/,0, -10,-20, 10,-20);)

gives

How can I get

?

Answers

  • edited February 2018
    colorMode(HSB, 6.0, 1.0, 1.0, 1.0);
    background(0); 
    size(500, 500);
    strokeWeight(1);
    stroke(1,1,1,0.5);
    fill(1,1,1,0.5);
    translate(100,100);
    line(0,-21,0,-50);
    noStroke();
    triangle(0.5,0, -10,-20, 10,-20);
    
  • Thanks but if you look closely at your version, the joint is the wrong colour.

    I would like a general solution that doesn't involve trying to make pixel-perfect kluging of coordinates, since in my experience, this never works properly in Processing due to Processings pixel inaccuracies (see my line 10 for example) and fails completely on strokeWeight and cap.

  • edited February 2018 Answer ✓

    I would like a general solution that doesn't involve trying to make pixel-perfect kluging of coordinates.

    One general solution is:

    1. create a buffer, e.g. PGraphics.
    2. Draw your overlapping shapes to the buffer in a solid, non-alpha color. Overlaps will be merged with no alpha... because there is no alpha.
    3. Set tint with an alpha argument -- e.g. tint(255,128)
    4. render your buffer to the canvas using image() at the desired level of alpha.

    This supports anything that can be rendered on a PGraphics -- strokeWeight, cap, etc.

  • Use a PShape with 7 points like =>

  • edited February 2018

    Jeremy, Thanks. That works.

    void setup()
    {
      colorMode(HSB, 6.0, 1.0, 1.0, 1.0);
      background(0); 
      size(300, 300);
    
      // Draw semi-transparent arrow - suffers from shaft showing through head
      pushMatrix();
      strokeWeight(1);
      stroke(1, 1, 1, 0.5);
      fill(1, 1, 1, 0.5);
      translate(60, 60);
      line(0, 0, 0, -50);
      noStroke();
      triangle(0.5 /**partial workaround for triangle assymetry*/, 0, -10, -20, 10, -20);
      popMatrix();
    
      // Draw semi-transparent arrow - no show-through
      PGraphics  b = createGraphics(width, height);
      b.beginDraw(); 
    
      b.background(0);
      b.colorMode(HSB, 6.0, 1.0, 1.0, 1.0);
      b.strokeWeight(1);
      b.stroke(1, 1, 1, 1);
      b.fill(1, 1, 1, 1);
      b.translate(60, 60);
      b.line(0, 0, 0, -50);
      b.noStroke();
      b.triangle(0.5 /**partial workaround for triangle assymetry*/, 0, -10, -20, 10, -20);
    
      b.endDraw();
    
      tint(0,0,1, .5);
      image(b, 100, 0);
    }
    

    gives

    Now, how can I streamline the fixed version's source to be nearer the compactness of the original? E.g. can I replace all the b. on the calls with a 'using' block or something?

    Or can I omit the b. and write to the default graphics object, then pull that result back into my PGraphics?

  • What you are creating, essentially, is a finished image of an arrow. You are already only doing this once, because it's happening in setup().

    What you can do next is to retain this image for later use. At the global level, create a PImage to save the arrow's image in:

    PImage img_arrow;
    

    Then in setup, after you have created the image, save a copy of it:

    img_arrow = b.get();
    

    Now if you want to draw this arrow multiple times, you can draw that image:

    image( img_arrow, 20, 20);
    image( img_arrow, 20, 120);
    image( img_arrow, 120, 20);
    image( img_arrow, 120, 120);
    

    Try it yourself!

  • TfGuy44, if that was an answer to my last question, I'm confused. What I'm asking for an improvement in the source, not in the functionality.

  • edited February 2018

    with a 'using' block or something

    Hmm.

    Can you say more about what you mean by this? I'm not sure what language you are thinking of that has "using" as a keyword, or if not a keyword, what kind of control structure you have in mind when you say "using."

    I'm also not sure what you mean by an "improvement" -- is it that you find calling methods on an object aesthetically unappealing, and you prefer when all of your code looks like function calls? What specifically don't you like that you are trying to change (without changing functionality)?

  • Sorry, for 'using' I should have written 'with' as in e.g. Visual BASIC.

    is it that you find calling methods on an object aesthetically unappealing

    More, doesn't accord with the DRY principle.

    What specifically don't you like that you are trying to change

    Repeating 'b.' on every method call.

    I see methods without '.' refer to the output PGraphics. Can I point this default to bmy own PGraphics?

  • edited February 2018

    Got it -- thanks for that example.

    Keep in mind that Processing code becomes Java before it is actually executed. This allows the underlying language to be simplified for learners; for example, background(0) looks like a function, but when it becomes Java it is actually [EDIT] passed through PApplet to a method of the main sketch canvas object -- g.background(0);. That isn't a feature of Java, that is a special "shorthand" feature of Processing, and it only works because the Processing preprocessor assumes that if you don't say the object, the object you mean is g (g is the name of the default canvas). There isn't a mechanism for swapping the default because of how the preprocessor works (I don't think).

    To keep things as clean as possible wrap reused PGraphics method sequences in a function that accepts a PGraphics as an object. It still explicitly names the object on each line, but it makes the code reusable.

    void myFunc (PGraphics pg){
      pg.stroke(1, 1, 1, 1);
      pg.fill(1, 1, 1, 1);
      pg.translate(60, 60);
      // etc.
    }
    

    But this doesn't eliminate the fact that you have to name an object to call its method --they still aren't functions. Perhaps more experienced Java developers on the forum know ways of calling a method without specifying its object -- I'm not sure offhand how it is possible without a creating a bunch of your own global functions hard-mapped to each PGraphics method.

    Some libraries make it possible to attach multiple methods calls to one object by using method call chaining, like this:

    object.stroke(1).fill(0).translate(60,60);
    

    ...but the Processing API isn't built like this (methods don't return self, they return void).

  • edited February 2018

    ...background(0); looks like a function; but when it becomes Java it is actually a method of the main sketch canvas object -- g.background(0);.

    @jeremydouglass, that specific background(), and many others btW, isn't a method of class PGraphics, but PApplet's. @-)

    I'm not sure offhand how it is possible without a creating a bunch of your own global functions hard-mapped to each PGraphics method.

    Actually, pretty much most of the methods from class PGraphics are also mirrored in class PApplet as mere wrappers; which in turn, simply rely on PApplet::g field in order to invoke those PGraphics' corresponding methods. 8-}

    That is, each instance of class PApplet is bound to 1 PGraphics main canvas via its field g. $-)

    BtW, a more formal way to get PApplet::g field is by calling PApplet::getGraphics() method. ;)

  • edited February 2018

    ... and it only works because the Processing's preprocessor assumes that if you don't say the object, the object you mean is g ...

    Although PDE's preprocessor indeed modifies our ".pde" sketches in order to turn them into 1 actual compilable ".java" valid source code, how much it's actually changed is surprisingly very little. :\">

    We can check that out by exporting our sketches and peek at its output ".java" file. *-:)

    The reason we can directly access almost all of the PApplet API's members raw, w/o needing the operator ., is due to the fact that all ".pde" files are concatenated together by the preprocessor as 1 unified ".java" file, all wrapped up as 1 subclass which extends PApplet. :-bd

    For most programming languages, for example JS and Python, we'd still need to prefix each member of that class w/ some variable which represents the current instance which is accessing them.

    For JS, just like Java, C, etc., that instance variable is the keyword this.
    Python doesn't have a specific keyword for it. But its accepted convention is to use self for it. >:)

    However, alike JS and Python, languages like Java & C# don't demand we boilerplate everything w/ this., as long as some local variable or parameter doesn't temporarily overshadow a class member's name. \m/

    For example, if we wish, we coulda written background(0); as this.background(0); instead. =P~
    But why do that when Java graciously free us from such repetitive endeavor. :))

  • Thanks both. GoToLoop's explanation of the internals suggests my override could be obtained only by wrapping in another sub-class, which doesn't sound great. So I'll stick to stating the object on every call.

  • edited February 2018

    BtW, JS & Python got their own using as keyword statement with (): :>
    https://Developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with

    However, at least for JS, besides being deprecated, it cancels any runtime optimization as well! :-SS

Sign In or Register to comment.