Shape union in processing? - processing

I'm learning processing and trying to create an SVG file containing a complex shape (path) which should be a union of a hexagon and a rectangle. But, unfortunately, I can't find any set operations on PShape.

I found useful ErraticGenerator's answer on Processing forum. The idea is to use g.js library mixed with p5js drawing methods.
Include the script in index.html:
<script src="https://cdn.rawgit.com/nodebox/g.js/master/dist/g.min.js"></script>
Sketch example:
function setup() {
createCanvas(400, 400)
noLoop()
}
function draw() {
let rect = g.rect(50, 50, 100, 100)
let ellipse = g.ellipse(100, 100, 100, 100)
let union = g.compound(rect, ellipse, 'union')
union.fill = null
union.stroke = 'red'
union.draw(drawingContext)
}
This also works in combo with p5.js-svg!

Related

How do I resize a p5.Graphic object?

In p5.js resizeCanvas(x, y) is a function for p5 objects but if I made a p5.Graphics object, can I resize it with a similar function?
Interestingly, p5.Graphics objects can run resizeGraphics() but nothing happens (including no error) and the height and width remain the same in the console.
g = createGraphics(50, 50); //creates p5.Graphics
g.resizeCanvas(100, 100); //fails: silently without error
g.resize(100, 100); //fails: resize has not been defined
Is there another function or would I need to actually extract the cooresponding graphics canvas and call a native javascript function instead?
Thanks!
If you want to resize a P5.Graphics, you can just create a new one, then draw the old one to the new one.
Here is an example:
var pg;
function setup() {
createCanvas(1000, 1000);
pg = createGraphics(100, 100);
pg.background(100);
pg.noStroke();
pg.ellipse(pg.width/2, pg.height/2, pg.width, pg.height);
}
function draw() {
background(200);
image(pg, 0, 0);
}
function mouseClicked(){
var newPG = createGraphics(mouseX, mouseY);
newPG.image(pg, 0, 0, newPG.width, newPG.height);
pg = newPG;
}
Using resizeCanvas on a graphics object created with "createGraphics" seems to stretch the graphics rather than resize the buffer.
Right now the best way to resize a graphics object is to simply set the width and height properties directly.
// Some graphics object
let graphics = createGraphics(w,h);
// Now simply resize like this
graphics.width = gWidth;
graphics.height = gHeight;
As suggested here:
https://github.com/processing/p5.js/issues/2064#issuecomment-315503533

How to execute a function when using the createGraphics function in P5js?

I want to use the createGraphics function to draw something on another screen... and then paste that into my main sketch..
in the docu, the example they give is doing something like:
var vignette;
function setup(){
createCanvas(710, 400);
vignette = createGraphics(400, 250);
}
function draw(){
ellipse(mouseX, mouseY, 60, 60);
pg.background(51);
pg.noFill();
pg.stroke(255);
pg.ellipse(mouseX-150, mouseY-75, 60, 60);
//Draw the offscreen buffer to the screen with image()
image(pg, 150, 75);
}
But what i want to do is more complex than pg.background(51)
I want to run this function which creates a radial gradient:
function drawGradient() {
for (let r = canvasX; r > 0; --r) {
let lightnes = map(r,0,canvasX,360,0)
fill(360, 360, lightnes)
ellipse(0, 0, r, r)
}
}
But if i do vignette.drawGradient() i get the error: vignette.drawGradient is not a function...
So how can i then execute things like whats inside the drawgradient function inside the createGraphics function?
Here is the codepen: https://codepen.io/giorgiomartini/pen/ZJjWbw?editors=0010
You wouldn't put anything "inside" the createGraphics() function. The createGraphics() function returns an instance of p5.Renderer. You then call functions on that instance.
You were going in the right direction by trying to call vignette.drawGradient(), but like you've discovered, p5.Renderer does not contain a drawGradient() function. Only your sketch contains that function, because it's something you created.
p5.Renderer contains the drawing functions: stuff like background(), fill(), rect(), and ellipse(). So if you want to draw your gradient to your vignette renderer, you have to call the functions that actually draw things on your vignette variable. Like this:
function drawGradient() {
for (let r = canvasX; r > 0; --r) {
let lightnes = map(r,0,canvasX,360,0)
vignette.fill(360, 360, lightnes)
vignette.ellipse(0, 0, r, r)
}
}

EaselJS: Changing a Shape Objects Dimensions

Total noob question. I've been abusing Google and for some reason, have surprisingly not been able to find anything regarding this...? I feel like I'm missing something here. :P
I currently have a resize() function that modifies the canvas dimensions to the size of the window. In a minimal example (which also uses jQuery), I have a variable that references my Shape object. According to the documents, the Shape Object does not include a width & height property. What is the most efficient way of resizing a Shape Object? Removing/redrawing dynamically?
This is what I have:
var stage;
var bgColor;
$(document).ready(function(){
init();
});
function init()
{
stage = new createjs.Stage("canvasStage");
bgColor = new createjs.Shape();
bgColor.graphics.beginFill("#000000").drawRect(0,0, stage.canvas.width, stage.canvas.width);
stage.addChild(bgColor);
$(window).resize(function(){windowResize();});
windowResize();
}
function windowResize()
{
stage.canvas.width = $(window).width();
stage.canvas.height = $(window).height();
//bgColor.width = $(window).height();// No width property
//bgColor.height = $(window).height();// NO height property
stage.update();}
You can use the shape's scaleX and scaleY to scale the shape.
Note: The Shape Object extends the DisplayObject so you might also want to look at the DisplayObject docs for many more useful properties/methods.
myShape.scaleX=1.2;
myShape.scaleY=1.2;
as mentioned by #markE, only scaleX and scaleY are available.
A way to work around the problem is to instantiate shapes with 1px width and 1px height:
mc_bg = new createjs.Shape();
mc_bg.graphics.beginFill("#CCCCCC").drawRect(0,0,1,1);
stage.addChild(mc_bg);
mc_left = new createjs.Shape();
mc_left.graphics.beginFill("#333333").drawRect(0,0,1,1);
stage.addChild(mc_left);
mc_circle = new createjs.Shape();
mc_circle.graphics.beginFill("#888888").drawCircle(0,0,1,1);
stage.addChild(mc_circle);
Allowing to set your dimensions with pixel units without conversion directly by using scaleX and scaleY before rendering:
mc_bg.scaleX = stage_width;
mc_bg.scaleY = stage_height;
mc_left.scaleX = stage_width/2;
mc_left.scaleY = stage_height;
mc_circle.x = stage_width/4;
mc_circle.y = stage_width/4;
mc_circle.scaleX = stage_width/4;
mc_circle.scaleY = stage_width/4;
stage.update();
See fiddle example with live resizing on window resize: https://jsfiddle.net/jckleinbourg/go3fs1zt/
Conversion methods to change scale --> dimensions and vice versa:
function dimToScale(origDim, desiredDim)
{
return desiredDim / origDim;
}
function scaleToDim(origDim, scale)
{
return scale * origDim;
}

Fixed floating shape on kinetic.js stage

I have tried to find a solution on how to implement fixed score table to visible stage (canvas) area by using kineticjs library layers and stages. The basic idea is that shape layer can be scrolled and stage is wider than the visible browser area but the score table should be all the time fixed to certain position on the visible area like in many casual games.
What would be the best approach on creating this kind of fixed always visible shapes to stage/canvas? Is there a way to refer to visible area xy position?
Your best bet for making a fixed position group is either to use a second layer on the stage or to create two groups for your objects - one that remains in place and another that can be moved. I'd go with the dual-layer approach as this allows you to redraw the layers separately so that when one has updated information, you don't need to redraw the other.
cvsObj.page = new Kinetic.Layer();
cvsObj.dynamic = new Kinetic.Layer();
cvsObj.stage.add(cvsObj.page);
cvsObj.stage.add(cvsObj.dynamic);
I took the two layer approach but actually my challenge was how to implement 'page' static behavior. I figured out that as it seems that Kinetic layers/stages do not offer methods for this I need to use other means to make 'page' layer on top of viewable area. I used jquery scrollTop() and scrollLeft()functions for this. And layer method .setAbsolutePosition() is used to update position for whole layer. Here pageLayer position is updated and dynamicLayer not so naming might be bit confusing but reflects what you see on browser.
Here is a simple example what does what I was looking for. Now the green rectangle moves when you scroll stage and the red rectangle and the x/y position counter stay on visible area. I attach this solution here with tutorial type of content as it might be useful.
<script>
window.onload = function() {
var stage = new Kinetic.Stage({
container: 'container',
width: 1600, height: 1600
});
var layerDynamic = new Kinetic.Layer();
var layerPage = new Kinetic.Layer();
var rectGreen = new Kinetic.Rect({
x: 239, y: 75, width: 100, height: 50, fill: 'green'
});
var rectRed = new Kinetic.Rect({
x: 100, y: 75, width: 100, height: 50, fill: 'red'
});
var simpleText = new Kinetic.Text({
x: 1, y: 1, text: "Init", textFill: "black"
});
layerDynamic.add(rectGreen);
layerPage.add(rectRed);
layerPage.add(simpleText);
$(document).scroll(function(e) {
var visibleXTop = $(this).scrollTop();
var visibleYTop = $(this).scrollLeft();
simpleText.setAttrs({text: visibleXTop + ' ' + visibleYTop});
layerPage.setAbsolutePosition(visibleYTop , visibleXTop);
layerPage.draw();
});
stage.add(layerDynamic);
stage.add(layerPage);
};
</script>

HTML 5 Canvas Mouse over event on element (show tooltip)

I am working on a visualization project. Based on my data I am plotting hundreds of small circle on canvas. I want to add a mouse over event so that whenever a mouse is the enclosing area of a circle it will show some node property from my data as a tool tip or as text on the canvas.
My current drawCircle method
function drawCircle(canvas,x,y,r)
{
canvas.strokeStyle = "#000000";
canvas.fillStyle = "#FFFF00";
canvas.lineWidth = 2;
canvas.beginPath();
canvas.arc(x,y,r,0,Math.PI*2,true);
canvas.stroke();
canvas.fill();
canvas.closePath();
}
I have looked into kinetic.js
But can't figure it out how I can call my drawCircle [repetitively] method using their library.
Any help will be highly appreciated.
If you still want to use KineticJS, you would put the Kinetic shape stuff inside your drawCircle routine. This is basically pulled out of their tutorial and stripped down:
function drawCircle(stage,x,y,r) {
var circle = new Kinetic.Shape(function(){
var context = this.getContext();
// draw the circle here: strokeStyle, beginPath, arc, etc...
});
circle.addEventListener("mouseover", function(){
// do something
});
stage.add(circle);
}
If you don't want to use KineticJS after all, you will need to remember for yourself the positions and radii of every circle you drew, and then do something like this:
canvas.onmouseover = function onMouseover(e) {
var mx = e.clientX - canvas.clientLeft;
var my = e.clientY - canvas.clientTop;
// for each circle...
if ((mx-cx)*(mx-cx)+(my-cy)*(my-cy) < cr*cr)
// the mouse is over that circle
}

Resources