Draw line with arrow cap - javascript

I am trying to draw a line with arrow at the tip of the line. But i am unable to complete it. Can any one help me.
I tried adding triangle with line but unfortunately it wont work while dynamically drawing. This is what i tried along with line.
var myPath;
function onMouseDown(event) {
myPath = new Path();
myPath.strokeColor = 'black';
}
function onMouseDrag(event) {
myPath.add(event.point);
}
function onMouseUp(event) {
var myCircle = new Path.RegularPolygon(event.point, 3, 10);
myCircle.strokeColor = 'black';
myCircle.fillColor = 'white';
}

you draw line with arrow with this code ,
paper.Shape.ArrowLine = function (sx, sy, ex, ey, isDouble) {
function calcArrow(px0, py0, px, py) {
var points = [];
var l = Math.sqrt(Math.pow((px - px0), 2) + Math.pow((py - py0), 2));
points[0] = (px - ((px - px0) * Math.cos(0.5) - (py - py0) * Math.sin(0.5)) * 10 / l);
points[1] = (py - ((py - py0) * Math.cos(0.5) + (px - px0) * Math.sin(0.5)) * 10 / l);
points[2] = (px - ((px - px0) * Math.cos(0.5) + (py - py0) * Math.sin(0.5)) * 10 / l);
points[3] = (py - ((py - py0) * Math.cos(0.5) - (px - px0) * Math.sin(0.5)) * 10 / l);
return points;
}
var endPoints = calcArrow(sx, sy, ex, ey);
var startPoints = calcArrow(ex, ey, sx, sy);
var e0 = endPoints[0],
e1 = endPoints[1],
e2 = endPoints[2],
e3 = endPoints[3],
s0 = startPoints[0],
s1 = startPoints[1],
s2 = startPoints[2],
s3 = startPoints[3];
var line = new paper.Path({
segments: [
new paper.Point(sx, sy),
new paper.Point(ex, ey)
],
strokeWidth: 1
});
var arrow1 = new paper.Path({
segments: [
new paper.Point(e0, e1),
new paper.Point(ex, ey),
new paper.Point(e2, e3)
]
});
var compoundPath = new paper.CompoundPath([line, arrow1]);
if (isDouble === true) {
var arrow2 = new paper.Path({
segments: [
new paper.Point(s0, s1),
new paper.Point(sx, sy),
new paper.Point(s2, s3)
]
});
compoundPath.addChild(arrow2);
}
return compoundPath;};
use
tool.onMouseDrag = function (event) { this.item = new paper.Shape.ArrowLine(event.downPoint.x, event.downPoint.y, event.point.x, event.point.y);
this.item.removeOnDrag();}

There is an example code in paperjs refrence which draws an arrow at the end of a vector.
Have a look at: http://paperjs.org/tutorials/geometry/vector-geometry/ (scroll all the way down to the end of the page)

A simple approach is to create a group that consists of the vector itself (the line) and the arrow head. lineStart and lineEnd are the points where the line of the arrow starts and ends.
// parameters
headLength = 10;
headAngle = 150;
lineStart = new Point(200, 200);
lineEnd = new Point (250, 250);
tailLine = new Path.Line(lineStart, lineEnd);
tailVector = lineEnd - lineStart;
headLine = tailVector.normalize(headLength);
arrow = new Group([
new Path([lineStart, lineEnd]),
new Path([
lineEnd + headLine.rotate(headAngle),
lineEnd,
lineEnd + headLine.rotate(-headAngle)
])
]);
arrow.strokeColor = 'black';
And, as previously mentioned, if you want to recreate it each time then you can make the previous code a function, something like:
// parameters
var headLength = 10;
var headAngle = 150;
var arrowColor = 'black';
// the arrow
var arrow = null;
function drawArrow(start, end) {
var tailLine = new Path.Line(start, end);
var tailVector = end - start;
var headLine = tailVector.normalize(headLength);
arrow = new Group([
new Path([start, end]),
new Path([
end + headLine.rotate(headAngle),
end,
end + headLine.rotate(-headAngle)
])
]);
arrow.strokeColor = arrowColor;
}
tool.onMouseDrag = function(e) {
if (arrow) {
arrow.remove();
}
drawArrow(e.downPoint, e.point);
}
Here is a sketch of the previous code sketch.paperjs.org

Here's an example, where I extend paper.Group.
In my example I avoid redrawing the arrow on each mousedrag. I create once on mousedown and then transform the line and the arrow parts to the appropriate position/rotation on each mousedrag.
Note: I'm fairly certain the following code can be improved a lot.
Drawing via mouse events
'use strict'
/* Arrow Class extends Group */
const Arrow = paper.Group.extend({
initialize: function (args) {
paper.Group.call(this, args)
this._class = 'Arrow'
this._serializeFields = Object.assign(this._serializeFields, {
from: null,
to: null,
headSize: null
})
this.from = args.from
this.to = args.to || args.from
this.headSize = args.headSize
// #NOTE
// `paper.project.importJSON()` passes the deserialized children
// (the arrow parts) to the `Group` superclass so there's no need to
// create them again.
if (this.children.length)
return
this.addChildren([
new paper.Path({
...args,
segments: [
this.from,
this.from
]
}),
new paper.Path({
...args,
segments: [
this.from,
this.from
]
}),
new paper.Path({
...args,
segments: [
this.from,
this.from
]
})
])
this.update(this.to)
},
update: function (point) {
const angle = this.from.subtract(point).angle - 90
this.children[0].lastSegment.point = point
this.children[1].firstSegment.point = point
this.children[1].lastSegment.point = point.add(
this.headSize,
this.headSize
)
this.children[2].firstSegment.point = point
this.children[2].lastSegment.point = point.add(
-this.headSize,
this.headSize
)
this.children[1].rotate(angle, point)
this.children[2].rotate(angle, point)
return this
}
})
paper.Base.exports.Arrow = Arrow
/* Usage */
paper.setup(document.querySelector('canvas'))
const tool = new paper.Tool()
let arrow
tool.onMouseDown = e => {
arrow = new Arrow({
from: e.point,
headSize: 10,
strokeWidth: 1,
strokeColor: '#555',
strokeCap: 'round'
})
}
tool.onMouseDrag = e => {
arrow.update(e.point)
}
canvas {
display: block;
width: 100%;
height: 100%;
margin: 0;
background: #fafafa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.min.js"></script>
<canvas resize="true"></canvas>
... or just draw to a static position
If you want to just draw the arrow (without using mouse events), just do the following:
const arrow = new Arrow({
from: new paper.Point(100, 100),
to: new paper.Point(200, 200),
headSize: 10,
strokeWidth: 1,
strokeColor: '#555',
strokeCap: 'round'
})

Related

ThreeJS scene keeps accelerating as it goes on

Me and my friend have been working on a uni assignment - a simple Pacman clone - in ThreeJS (mandatory) and we've had this problem since pretty much the beginning. As the scene keeps running, it starts going faster and faster with each second. It's not like there becomes less stuff to render, everything is pretty much the same.
import * as THREE from '././../three.js-master/build/three.module.js';
function main() {
const canvas = document.querySelector('#canva');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
/* Camera */
const fov = 40;
const aspect = window.innerWidth / window.innerHeight;
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// I want to go for that angled look
// Angled enough to show depth, but still have everything important visible
camera.position.set(0, 65, -45); //Man setting up the camera is fun and not at all painful
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
/* Lights */
const mainLight = new THREE.DirectionalLight(0xffffff, .85);
mainLight.position.set(0, 20, 0);
scene.add(mainLight);
mainLight.castShadow = true;
mainLight.shadow.mapSize.width = 2048;
mainLight.shadow.mapSize.height = 2048;
// const light = new THREE.PointLight(0xffffff, 1);
// light.position.set(30, 0, 30);
// scene.add(light);
/* Board */
const boardGeometry = new THREE.PlaneGeometry(50, 50);
const boardMaterial = new THREE.MeshToonMaterial({ color: 0xEEEEEE, side: THREE.DoubleSide });
const board = new THREE.Mesh(boardGeometry, boardMaterial);
board.rotation.x = Math.PI / 2; //The board must be placed flat on the x axis
scene.add(board);
/* Player */
const playerBox = new THREE.Box3() // Used to determine collisions
const playerGeometry = new THREE.BoxGeometry(1, 1, 1.5);
const playerMaterial = new THREE.MeshToonMaterial({ color: 0xAAAAAA });
const player = new THREE.Mesh(playerGeometry, playerMaterial);
player.geometry.computeBoundingBox(playerBox);
/* Players Axes helper */
const axesHelper = new THREE.AxesHelper(5);
player.add(axesHelper);
scene.add(player);
/* Box helper */
const playerHelper = new THREE.Box3Helper(playerBox, 0xffff00);
scene.add(playerHelper);
/* Enemy #1 */
const enemy1Box = new THREE.Box3()
const enemy1Geometry = new THREE.BoxGeometry(1, 1, 1);
const enemy1Material = new THREE.MeshToonMaterial({ color: 0x0000ff });
const enemy1 = new THREE.Mesh(enemy1Geometry, enemy1Material);
enemy1.position.x = 2
enemy1.geometry.computeBoundingBox(enemy1Box);
scene.add(enemy1);
const enemyHelper = new THREE.Box3Helper(enemy1Box, 0xffff00);
scene.add(enemyHelper);
/////////////////////
/* Cheese */
const smallCollectibleRadius = .4
const bigCollectibleRadius = .6
/* Standard cheese */
var cheeseList = []
var cheeseBoxList = []
const cheeseBox = new THREE.Box3();
const cheeseGeometry = new THREE.SphereGeometry(smallCollectibleRadius, 100, 100);
const cheeseMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, emissive: 0xffff00 });
var cheese = new THREE.Mesh(cheeseGeometry, cheeseMaterial); // Changed const to var so it could be set to null in order to get rid of after collecting
cheese.position.set(0, 0, 3)
scene.add(cheese);
cheese.geometry.computeBoundingBox(cheeseBox);
const cheeseHelper = new THREE.Box3Helper(cheeseBox, 0xffff00);
scene.add(cheeseHelper);
/* SuperCheese */
const superCheeseGeometry = new THREE.SphereGeometry(bigCollectibleRadius, 100, 100);
const superCheeseMaterial = new THREE.MeshToonMaterial({ color: 0xcb6600, emissive: 0x0000ff });
const superCheese = new THREE.Mesh(superCheeseGeometry, superCheeseMaterial);
superCheese.position.set(0, 0, 5)
scene.add(superCheese);
/* Outer wall generation */
const walls = [];
const wallGeometry = new THREE.BoxGeometry(1, 1, 1);
const wallMaterial = new THREE.MeshToonMaterial({ color: 0xAAAAAA });
//Yes I know the wall being split into blocks might not look good for now
//Please trust me, it will make more sense later
//Top
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(24.5 - i, 0.5, 24.5)
walls.push(wall);
}
//Right
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(-24.5, 0.5, 24.5 - i)
walls.push(wall);
}
//Bottom
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(-24.5 + i, 0.5, -24.5)
walls.push(wall);
}
//Left
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(24.5, 0.5, -24.5 + i)
walls.push(wall);
}
//Tried to do the four of them as a single double loop
//Might try again later
for (var i = 0; i < walls.length; i++) scene.add(walls[i]);
//////////////////////////
function makeCoins(num, cheeseList_t = cheeseList, cheeseBoxList_t = cheeseBoxList) { //I know these names break the convention.
const smallCollectibleRadius = .4
const bigCollectibleRadius = .6
for (let i = 0; i < num; i++) {
console.log("I'm a fake cheese!")
const cheeseBox = new THREE.Box3();
const cheeseGeometry = new THREE.SphereGeometry(smallCollectibleRadius, 100, 100);
const cheeseMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, emissive: 0xffff00 });
var cheese = new THREE.Mesh(cheeseGeometry, cheeseMaterial);
cheese.position.set(0, 0, (6 + i))
cheese.userData.idd = i // it's in a dict form, {"idd":i}
scene.add(cheese);
cheese.geometry.computeBoundingBox(cheeseBox);
const cheeseHelper = new THREE.Box3Helper(cheeseBox, 0xffff00);
scene.add(cheeseHelper);
cheeseList_t.push(cheese)
cheeseBoxList_t.push(cheeseBox)
}
}
makeCoins(2) // IT WORKS, HOLY COW
console.log(cheeseList[1])
function checkCollision(box) {
/* Code sucks but works and will by changed in future anyway */
var collision = playerBox.intersectsBox(box);
if (collision == true) {
return true
}
}
function collect(item) {
/* TODO: Collecting cheeses */
}
var points = 0;
function updatePoints(amount) {
points += amount
}
document.addEventListener("keypress", function (event) {
/* Additional player movement's listener that doesn't throw 2137 msgs in the console.
It would be great to make it the only one player movement's listener */
if (checkCollision(enemy1Box)) {
/**/
console.log("You bumped into enemy1!")
}
if (cheese !== null && !('consumed' in cheese.userData) && checkCollision(cheeseBox)) {
/* This will go to collect() function once finished */
console.log("Yummy cheese!")
scene.remove(cheese)
cheese.userData.consumed = true // good to know there is this userData feature
cheese = null
updatePoints(1)
}
/* For testing purposes */
var coinBox = cheeseBoxList[0]
if (checkCollision(coinBox)){
console.log("HOLY GUACAMOLE, IT WORKS") // IT REALLY DOES!
}
});
/////////////////////////////////////////////////////////////////////////////////////////////////
/* Render stuff */
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
const speed = 0.0005
const rotSpeed = 0.00005
const dir = new THREE.Vector3();
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
/* Updating boxes */
enemy1Box.copy(enemy1.geometry.boundingBox).applyMatrix4(enemy1.matrixWorld);
playerBox.copy(player.geometry.boundingBox).applyMatrix4(player.matrixWorld);
if (cheese !== null && !('consumed' in cheese.userData)) {
cheeseBox.copy(cheese.geometry.boundingBox).applyMatrix4(cheese.matrixWorld);
}
/* TEST */
var coin = cheeseList[0]
var coinBox = cheeseBoxList[0]
coinBox.copy(coin.geometry.boundingBox).applyMatrix4(coin.matrixWorld); // HOLY COW, IT SEEMS TO WORK
/* I'm afraid it'll lag everything but I can't think of better way of doing this;
this MUST be in render(), otherwise it won't update, but for loop in HERE... ugh
I will not run this. I care for my new PC. I don't want it to burn. I don't care for yours tho. Sorry not sorry, mate
IT'S NOT FINISHED YET, DON'T THINK OF RUNNING THIS
for (let i = 0; i < num; i++) {
coin = coinList[i]
coinBox = coinBoxList[i]
coinBox.copy(coin.geometry.boundingBox).applyMatrix4(coin.matrixWorld);
}
*/
document.getElementById("points").innerHTML = points
document.addEventListener("keypress", function (event) {
if (event.keyCode == 119) { //W going forward
if (checkCollision(enemy1Box)) {
console.log("Can't go any further!") // TODO: Delete in the future
} else {
player.getWorldDirection(dir);
player.position.addScaledVector(dir, speed);
}
}
if (event.keyCode == 115) { //S going backward
player.getWorldDirection(dir);
player.position.addScaledVector(dir, -speed);
}
if (event.keyCode == 97) { //A left rotation
player.rotation.y += rotSpeed
}
if (event.keyCode == 100) { //D right rotation
player.rotation.y -= rotSpeed
}
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
You’re adding keypress event listeners inside your render loop. This means that by the time 60 frames have gone by, you’ll have 60 event listeners doing the same thing. That’s why you’re seeing your scene speeding up: the more frames have passed, the more event listeners you’ve added.
Just initialize those events outside the render loop, and you’ll see the issue go away.

Trying to use createGraphics() in p5.js along with eye tracker and it isn't working

I'm still a beginner coder and I'm making a project where the eye positions (networked by socket) of two clients when landing on the same positions on the canvas would play the music note it lands on. I'm still in the beginning stages of this project and currently, I'm trying to draw the client's eye position on the canvas while the grid of music notes on a p5 renderer object. I had coded the grid to add an ellipse to where the mouse has clicked. The grid is successfully drawn but I no longer can interact with the grid i.e add or remove ellipses on click.
So now I'm a bit lost on how to solve this issue. Before when the eye was being drawn on p5.renderer I also tried the clear() function to get rid of the trails but it didn't work.
So I have two problems 1) trying to get rid of eye position trails and 2) using create graphics with mouse pressed function not working in code below.
// NETWORKED + EYE VARIABLES
let socket = io();
let ctracker;
let clients = {};
let data = {};
let w = 1200;
let h = 600;
let leftEyeX, leftEyeY, rightEyeX, rightEyeY;
let cnvPosX;
let cnvPosY;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of no.s that we can use to make beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the instruments to the to make it into a machine i.e on/off
let bpmCTRL;
let beatLength; // how big is sequence before it starts looping
let cellSize;
let cnv;
let overlayCnv;
let bg, largeText,smallText, MleftEye, MRightEye;
let row1 =[];
function preload() {
// background
bg = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142");
//main game instruction
largeText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278");
// small game description
smallText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354");
//sound files
bell = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294", () => {}
);
};
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w,h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup canvas
ctracker = new clm.tracker();
ctracker.init(pModel);
ctracker.start(videoInput.elt);
noStroke();
socket.on('getUpdate', function(data){
clients[data.name] = data;
});
cnv = createCanvas(w, h);
cnv.position(cnvPosX,cnvPosY)
overlayCnv = createGraphics(w,h);
// overlayCnv.position(cnvPosX,cnvPosY);
overlayCnv.mousePressed(canvasPressed);
beatLength = 6;
cellSize = 200;
numOfRows = 3;
// canvas for eyes
// basic structure of a DAW
// time is a sceduled delay in note play time
bPat = [0, 0, 0, 0, 0, 0];
b1Pat = [0, 0, 0, 0, 0, 0];
b2Pat = [0, 0, 0, 0, 0, 0];
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
console.log(selected);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong()
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.play(time); },
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.play(time);
},
b2Pat
);
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
drawMatrix();
///// user interact
function canvasPressed() {
console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
drawMatrix();
}
/// drawing the grid
function drawMatrix() {
overlayCnv.background(bg);
//line
overlayCnv.stroke(25,40,100);
overlayCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
overlayCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
overlayCnv.line(0, (i * height) / numOfRows, width, (i * height) / numOfRows);
}
//circle
overlayCnv.noStroke();
overlayCnv.fill(25,40,100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
image(overlayCnv, 0, 0);
}
function draw() {
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// draw ellipse at each position point
leftEyeX = positions[27][0];
leftEyeY = positions[27][1];
rightEyeX = positions[32][0];
rightEyeY = positions[32][1];
// ellipse(positions[i][0], positions[i][1], 8, 8);
fill(255);
ellipse(rightEyeX, rightEyeY, 18, 18);
fill(255);
ellipse(leftEyeX, leftEyeY, 18, 18);
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + '0'] = leftEyeX;
data[(27).toString() + "." + '1'] = leftEyeY;
data[(32).toString() + "." + '0'] = rightEyeX;
data[(32).toString() + "." + '1'] = rightEyeY;
// image(eyeCnv,0,0); // x, y relative to canvas x & y
}
}
/////// conditional to check if all files are loaded
function keyPressed() {
if (key === " ") {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax");
}
}
}
Nevermind, the error was just that background kept drawing in draw()

Trying to use mousePressed on a button and it ain't working?

I’m running into some errors. I want the user to select an eye player through clicking a button (either left eye or right eye) and then draw to the canvas only the selected input. I am using an eye tracker to draw some png files onto the canvas. I want them to draw only the left eye one if the user clicks on the left button.
I'm sorry for the extremely long code, I'm very new to code and don't know how to do it efficiently. The main variables I've been using for my problem is 'left' which is a button I created in setup() and in draw() I go through an array of points (clm eye tracker) and then save the left and right eye positions to objects, I then call the function mouse pressed on the left button.
// NETWORKED + EYE VARIABLES
let socket = io(); // new socket on client side
let ctracker; // eye tracker obj
let clients = {}; // client object
let data = {}; // data object
let w = 1200; // matrix width
let h = 600; // matrix height
let leftEye, rightEye; // main eye image
let SleftEye, SrightEye; // shadow eye image
let leftEyeX, leftEyeY, rightEyeX, rightEyeY; // current user eye data point vars
let cnvPosX, cnvPosY; // postion of main canvas
let playerLeft = {};
let playerRight = {};
let left,right;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of nums that we can use to make a sequence of beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the above (sound,array,phrase) into a machine (part) i.e on/off,looper
let beatLength; // how big is sequence before it starts looping
let cellSize; // each sqauare size contains beat
let row1 = []; // array of sounds in row 1
// DOM variables
let play; // play button
let bg, largeText, smallText; // image variables for game instructions and background
let cnv; // main canvas
let matrixCnv; // canvas that draws the matrix ( music user interface )
let instructCnv; // instruction canvas at the topmost part
let bpmCTRL; // beat per minute slider
// let previousCo = {
// left: {row: null, column: null},
// right: {row: null, column:null}
// };
function preload() {
// background
bg = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142"
);
//main game instruction
largeText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278"
);
// small game description
smallText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354"
);
// game main player left eye
leftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fleft-eye.png?v=1589284305903"
);
// game main player right eye
rightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fright-eye.png?v=1589284306011"
);
// game shadow player left eye
SleftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-left-eye.png?v=1589284306106"
);
// game shadow player left eye
SrightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-right-eye.png?v=1589284332165"
);
//sound files
bell = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294",
() => {}
);
}
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w, h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup tracker
ctracker = new clm.tracker();
ctracker.init(pModel); // feed it model pca 20
ctracker.start(videoInput.elt); // feed it video data
// open socket io for connections
socket.on("getUpdate", function(data) {
clients[data.name] = data;
});
instructCnv = createGraphics(w, h);
instructCnv.background(0);
matrixCnv = createGraphics(w, h); // matrix canvas of daw
cnv = createCanvas(w, h); // main canvas that the eys are being drawn on
cnv.position(cnvPosX, cnvPosY); // main. canvas position
matrixCnv.mousePressed(canvasPressed); //drawMatrix() function on matrix function
cnv.mousePressed(canvasPressed); // drawMatrix() function on main function
beatLength = 6; // number of beats it loops thru && columns of matrix
cellSize = 200; // size of square
numOfRows = 3; // rows of matrix
play = createButton('Play/Pause');
play.position(1300, 10);
play.size(80,30);
play.mousePressed(playAudio);
// basic structure of a Digital Audio Workstation
// time is a sceduled delay in note play time
bPat = [0, 1, 0, 0, 0, 0];
b1Pat = [0, 0, 1, 0, 1, 1];
b2Pat = [1, 0, 0, 1, 0, 0];
// random selection of sound(bell3 or bell) in row 1
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
selected.setVolume(0.1);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong();
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.setVolume(0.1);
bell1.play(time);
},
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.setVolume(0.1);
bell2.play(time);
},
b2Pat
);
// creating a new part that contains all the above
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
//button left
left = createButton('Left');
left.position(300,15);
// drawing the grid
drawMatrix();
}
function draw() {
image(matrixCnv, 0, 0);
// background(bg);
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// find in array each position point
playerLeft = {
x : positions[27][0],
y : positions[27][1]
playerRight = {
x : positions[32][0],
y : positions[32][1]
}
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + "0"] = playerLeft.x;
data[(27).toString() + "." + "1"] = playerLeft.y;
data[(32).toString() + "." + "0"] = playerRight.x;
data[(32).toString() + "." + "1"] = playerRight.y;
// update client details
data["name"] = socket.id;
data["updated"] = new Date().getTime();
socket.emit("update", data);
//recieving data by other client and drawing
var x, y;
for (var c in clients) {
if (c == socket.id) {
// if client is not the the current user
continue;
}
var points = clients[c];
// console.log(points)
var leftEyePoint = {
x: points['27.0'],
y: points['27.1']
};
var rightEyePoint = {
x: points['32.0'],
y: points['32.1']
};
image(SleftEye, leftEyePoint.x, leftEyePoint.y, 150, 100)
image(SrightEye, rightEyePoint.x, rightEyePoint.y, 150, 100)
// canvasEyed(leftEyePoint, rightEyePoint)
}
}
drawMatrix();
}
function drawLeft(x,y) {
push();
translate(x, y);
image(leftEye, 0, 0, 150, 100);
pop();
}
function drawRight() {
push();
translate(playerRight.x, playerRight.y);
image(rightEye, 0, 0, 150, 100);
pop();
}
/////// conditional to check if all files are loaded
function playAudio() {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax and wait for sound to load");
}
}
/// user interact
function canvasPressed() {
// console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
}
/// drawing the grid
function drawMatrix() {
matrixCnv.background(bg);
//line
matrixCnv.stroke(25, 40, 100);
matrixCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
matrixCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
matrixCnv.line(
0,
(i * height) / numOfRows,
width,
(i * height) / numOfRows
);
}
//circle
matrixCnv.noStroke();
matrixCnv.fill(25, 40, 100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
Dan O is right,
That is not the correct way of passing variables as arguments in callback functions.
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
It Should be:
left.mousePressed( function()=>{
drawLeft( playerLeft.x, playerLeft.y );
} );
This way you create a new function, an extra step inbetween. The function calls drawLeft(). Then pass that new function as the callback function. It's the same as doing this:
let afterMousePressed = function(){
drawLeft( playerLeft.x, playerLeft.y );
};
left.mousePressed( afterMousePressed );

drawImage with Canvas is sending image to the back of the canvas [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have been doing a little experimenting with canvas, creating drawings using lines shapes text etc, and inserting .png files. The inserting .png files is the bit that I cannot get to work.
Edit:
Undesired behaviour of this code: I load shapes to the graphics context, then load an image file to the graphics context, however when the graphics context is drawn, the image is at behind the shapes, despite being drawn last.
I wanted the image file to be at the top, in front of the shapes.
Desired behaviour: To bring image file to the front of the canvas, so it is not hidden by shapes drawn in the graphics context.
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
};
}
the function for drawing is called here:
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
code for the whole page is
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Hierarchical Modeling 2D</title>
<style>
#messagediv {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
background-color: indigo;
}
#canvasdiv {
position: absolute;
left: 0;
top: 0;
z-index: 10;
background-color: transparent;
}
</style>
<script type="text/javascript" src="rgbcolor.js"></script>
<script type="text/javascript">
"use strict";
var totalResources = 17;
var numResourcesLoaded = 0;
var images = {};
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
//
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
}
}
var canvas; // DOM object corresponding to the canvas
var graphics; // 2D graphics context for drawing on the canvas
var ctx; // 2D graphics context for drawing on the canvas
var myNumber = 0, myNumber2 = 0, myInterval, myInterval2, myelement, thisdiv, printx;
var mycoords = new Array();
var pcoords = new Array(); //coordinates of the portal.
//var pcoords = [[0,0], [50,300], [250,150]]; //coordinates of the portal.
var nocoords = 2;
var frameNumber = 0; // Current frame number.
var frameNumber2 = 0;
var sun;
var sun2;
var ground;
var world;
var pixelSize;
var wWidth;
var wHeight;
var portals = new Array("calendar1","alternativsearch","art1", "directory1");
var portalsval = new Array();
var portalsobj;
var leftj = new Array(3,1,4,2);
var forwards = "http://www.alternativworld.com";
// ---------------- Set Page Layout ----------------
// function to set size of canvas and location of portals
function pageLayout() {
var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];
wWidth = w.innerWidth || e.clientWidth || g.clientWidth;
wHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;
// Adjust wWidth and wHeight if ratio does not match scenary 7 by 5.
if (wWidth/wHeight != 7/5)
if (wWidth/wHeight > 7/5) {
var widthPortion = 5 * wWidth/wHeight;
wWidth = wWidth * 7 / widthPortion;
} else {
var heightPortion = 7 * wHeight/wWidth;
wHeight = wHeight * 5 / heightPortion;
}
var widthheight, localerror = false;
widthheight = Math.min(wWidth, wHeight);
if(widthheight < 400){
var localerror = true;
}
if (localerror == true)
alert("Warning, the page size of your browser or your screen resolution may be too small to correctly view this web page.");
var theCanvas = d.getElementById("theCanvas");
theCanvas.height = wHeight;
theCanvas.width = wWidth;
}
//Function to listen to the mouse events and see if a link is selected.
function doMouseDown(evt) {
var r = canvas.getBoundingClientRect();
var x = Math.round(evt.clientX - r.left);
var y = Math.round(evt.clientY - r.top);
alert(evt.clientX+ " " + evt.clientY);
for (var i = portals.length+1; i >= 0; i--) {
var p = pcoords[i];
if (Math.abs(p[0] - x) <= 50 && Math.abs(p[1] - y) <= 50) {
document.location.href = forwards;
return;
} else if (Math.abs(0 - x) <= 50 && Math.abs(0 - y) <= 50){
document.location.href = "http://www.alternativeuk.co.uk";
return;
}
}
}
// ---------------- The object-oriented scene graph API ------------------
/**
* The base class for all nodes in the scene graph data structure.
*/
function SceneGraphNode() {
this.fillColor = null; // If non-null, the default fillStyle for this node.
this.strokeColor = null; // If non-null, the default strokeStyle for this node.
}
SceneGraphNode.prototype.doDraw = function(g) {
// This method is meant to be abstract and must be
// OVERRIDDEN in any actual object in the scene graph.
// It is not meant to be called; it is called by draw().
throw "doDraw not implemented in SceneGraphNode"
}
SceneGraphNode.prototype.draw = function(g) {
// This method should be CALLED to draw the object
// represented by this SceneGraphNode. It should NOT
// ordinarily be overridden in subclasses.
graphics.save();
if (this.fillColor) {
g.fillStyle = this.fillColor;
}
if (this.strokeColor) {
g.strokeStyle = this.strokeColor;
}
this.doDraw(g);
graphics.restore();
}
SceneGraphNode.prototype.setFillColor = function(color) {
// Sets fillColor for this node to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
return this;
}
SceneGraphNode.prototype.setStrokeColor = function(color) {
// Sets strokeColor for this node to color.
// Color should be a legal CSS color string, or null.
this.strokeColor = color;
return this;
}
SceneGraphNode.prototype.setColor = function(color) {
// Sets both the fillColor and strokeColor to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
this.strokeColor = color;
return this;
}
/**
* Defines a subclass, CompoundObject, of SceneGraphNode to represent
* an object that is made up of sub-objects. Initially, there are no
* sub-objects.
*/
function CompoundObject() {
SceneGraphNode.call(this); // do superclass initialization
this.subobjects = []; // the list of sub-objects of this object
}
CompoundObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
CompoundObject.prototype.add = function(node) {
// Add node a subobject of this object. Note that the
// return value is a reference to this node, to allow chaining
// of method calls.
this.subobjects.push(node);
return this;
}
CompoundObject.prototype.doDraw = function(g) {
// Just call the sub-objects' draw() methods.
for (var i = 0; i < this.subobjects.length; i++)
this.subobjects[i].draw(g);
}
/**
* Define a subclass, TransformedObject, of SceneGraphNode that
* represents an object along with a modeling transformation to
* be applied to that object. The object must be specified in
* the constructor. The transformation is specified by calling
* the setScale(), setRotate() and setTranslate() methods. Note that
* each of these methods returns a reference to the TransformedObject
* as its return value, to allow for chaining of method calls.
* The modeling transformations are always applied to the object
* in the order scale, then rotate, then translate.
*/
function TransformedObject(object) {
SceneGraphNode.call(this); // do superclass initialization
this.object = object;
this.rotationInDegrees = 0;
this.scaleX = 1;
this.scaleY = 1;
this.translateX = 0;
this.translateY = 0;
}
TransformedObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
TransformedObject.prototype.setRotation = function(angle) {
// Set the angle of rotation, measured in DEGREES. The rotation
// is always about the origin.
this.rotationInDegrees = angle;
return this;
}
TransformedObject.prototype.setScale = function(sx, sy) {
// Sets scaling factors.
this.scaleX = sx;
this.scaleY = sy;
return this;
}
TransformedObject.prototype.setTranslation = function(dx,dy) {
// Set translation mounts.
this.translateX = dx;
this.translateY = dy;
return this;
}
TransformedObject.prototype.doDraw = function(g) {
// Draws the object, with its modeling transformation.
g.save();
if (this.translateX != 0 || this.translateY != 0) {
g.translate(this.translateX, this.translateY);
}
if (this.rotationInDegrees != 0) {
g.rotate(this.rotationInDegrees/180*Math.PI);
}
if (this.scaleX != 1 || this.scaleY != 1) {
g.scale(this.scaleX, this.scaleY);
}
this.object.draw(g);
g.restore();
}
/**
* A subclass of SceneGraphNode representing filled triangles.
* The constructor specifies the vertices of the triangle:
* (x1,y1), (x2,y2), and (x3,y3).
*/
function Triangle(x1,y1,x2,y2,x3,y3) {
SceneGraphNode.call(this);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
}
Triangle.prototype = new SceneGraphNode();
Triangle.prototype.doDraw = function(g) {
g.beginPath();
g.moveTo(this.x1,this.y1);
g.lineTo(this.x2,this.y2);
g.lineTo(this.x3,this.y3);
g.closePath();
g.fill();
}
/**
* Directly create a line object as a SceneGraphNode with a
* custom doDraw() method. line is of length 1 and
* extends along the x-axis from (0,0) to (1,0).
*/
var line = new SceneGraphNode();
line.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(1,0);
g.stroke();
}
/**
* Directly create a filled rectangle object as a SceneGraphNode with a
* custom doDraw() method. filledRect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5).
*/
var filledRect = new SceneGraphNode();
filledRect.doDraw = function(g) {
g.fillRect(-0.5,-0.5,1,1);
}
/**
* Directly create a rectangle object as a SceneGraphNode with a
* custom doDraw() method. rect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5). Only the
* outline of the square is drawn.
*/
var rect = new SceneGraphNode();
rect.doDraw = function(g) {
g.strokeRect(-0.5,-0.5,1,1);
}
/**
* Directly create a filled circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0).
*/
var filledCircle = new SceneGraphNode();
filledCircle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.fill();
}
var clickHere = new SceneGraphNode();
clickHere.doDraw = function(g) {
g.fillText("click here :)",0,0)
}
/**
* Directly create a circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0). Only the outline of the circle
* is drawn.
*/
var circle = new SceneGraphNode();
circle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.stroke();
}
var dog = new SceneGraphNode();
dog.doDraw = function(g) {
g.drawImage(images["dog-walking11"],-2, 2);
alert(images["dog-walking11"].name);
}
// -------------------- Specific to this application ----------------------------
/*
* Define two extra basic objects as SceneGraphNodes with custom doDraw() methods.
* One represents the ground, the other a vane for a windmill.
*/
var ground = new SceneGraphNode();
ground.doDraw = function(g) {
g.beginPath();
g.moveTo(0,-1);
g.lineTo(0,0.8);
g.lineTo(1.5,1.65);
g.lineTo(1.8,1.3);
g.lineTo(3,2.1);
g.lineTo(4.7,0.7);
g.lineTo(6.1,1.2);
g.lineTo(7,0.8);
g.lineTo(7,-1);
g.closePath();
g.fill();
}
var windmillVane = new SceneGraphNode();
windmillVane.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(0.5,0.1);
g.lineTo(1.5,0);
g.lineTo(0.5,-0.1);
g.closePath();
g.fill();
}
var world; // A SceneGraphNode representing the entire picture. This should
// be created in the createWorld() method.
var pixelSize; // The size of one pixel, in the transformed coordinates.
// This is used as the default width of a stroke.
var background = "#C8C8FF"; // A CSS color string giving the background color.
// the draw() function fills the canvas with this color.
var xleft = 0; // The requested xy-limits on the canvas, after the
var xright = 7; // coordinate transformation has been applied.
var ybottom = -1; // The transformation is applied in the draw() function.
var ytop = 4;
var frameNumber = 0; // Current frame number.
var cart; // TransformedObjects that are animated.
var wheel;
var sun;
var clickText1;
var clickText2;
var rotor;
/**
* Create the scene graph data structure. The global variable world must
* refer to the root node of the scene graph. This function is called in
* the init() function.
*/
function createWorld() {
pageLayout();
var i;
var sunTemp = new CompoundObject();
sunTemp.setColor("yellow"); // color for filled circle and light rays
for (i = 0; i < 12; i++) { // add the 12 light rays, with different rotations
sunTemp.add( new TransformedObject(line).setScale(0.75,0.75).setRotation(i*30) );
}
sunTemp.add( filledCircle ); // the face of the sun
sunTemp.add( new TransformedObject(circle).setColor("#B40000") ); // outlines the face
sun = new TransformedObject(sunTemp);
clickText1 = new TransformedObject(clickHere).setColor("#B40000").setScale(0.01,-0.01);
var wheelTemp = new CompoundObject();
wheelTemp.setColor("black"); // color for all but one of the subobjects
wheelTemp.add( new TransformedObject(filledCircle).setScale(2,2) );
wheelTemp.add( new TransformedObject(filledCircle).setScale(1.6,1.6).setColor("#CCCCCC") );
wheelTemp.add( new TransformedObject(filledCircle).setScale(0.4,0.4) );
for (i = 0; i < 12; i++) { // add the 12 spokes
wheelTemp.add( new TransformedObject(line).setRotation(i*30) );
}
wheel = new TransformedObject(wheelTemp);
var cartTemp = new CompoundObject();
cartTemp.setColor("red"); // color for the rectangular body of the cart
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(1.5,-0.1) );
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(-1.5,-0.1) );
cartTemp.add( new TransformedObject(filledRect).setScale(5,2).setTranslation(0,1) ); // the body of the cart
cart = new TransformedObject(cartTemp).setScale(0.3,0.3);
clickText2 = new TransformedObject(clickHere).setColor("yellow").setScale(0.01,-0.01);
var rotorTemp = new CompoundObject(); // a "rotor" consisting of three vanes
rotorTemp.setColor( "#C86464" ); // color for all of the vanes
rotorTemp.add( windmillVane );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(120) );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(240) );
rotor = new TransformedObject(rotorTemp);
var windmill = new CompoundObject();
windmill.setColor("#E0C8C8"); // color for the pole
windmill.add( new TransformedObject(filledRect).setScale(0.1,3).setTranslation(0,1.5) ); // the pole
windmill.add( new TransformedObject(rotor).setTranslation(0,3) ); // the rotating vanes
world = new CompoundObject();
world.setColor("#00961E"); // color used for the ground only
world.add(ground);
//world.add( new TransformedObject(filledRect).setScale(7,0.8).setTranslation(3.5,0).setColor("#646496") ); // road
//world.add( new TransformedObject(filledRect).setScale(7,0.06).setTranslation(3.5,0).setColor("white") ); // line in road
world.add( new TransformedObject(windmill).setScale(0.6,0.6).setTranslation(0.75,1) );
world.add( new TransformedObject(windmill).setScale(0.4,0.4).setTranslation(2.2,1.3) );
world.add( new TransformedObject(windmill).setScale(0.7,0.7).setTranslation(3.7,0.8) );
world.add( new TransformedObject(sun).setTranslation(5.5,3.3) );
world.add( new TransformedObject(clickText1).setTranslation(5.25,3.3) );
world.add( cart );
world.add( clickText2 );
//alert(2);
}
/**
* This will be called before each frame is drawn.
*/
function updateFrame() {
frameNumber++;
if (frameNumber>= 312){
frameNumber = 0;
frameNumber2 = 1;
}
cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0);
clickText2.setTranslation(-3.3 + 13*(frameNumber % 300) / 300.0, 0.25);
if (typeof(pcoords[5]) != 'undefined') {
pcoords[5][0] = (-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*canvas.width / (xright-xleft);
pcoords[5][1] = (0.25-ytop)*canvas.height / (ybottom-ytop);
}
wheel.setRotation(-frameNumber*3.1);
sun.setRotation(-frameNumber);
rotor.setRotation(frameNumber * 2.7);
}
// ------------------------------- graphics support functions --------------------------
/**
* Draw one frame of the animation. Probably doesn't need to be changed,
* except maybe to change the setting of preserveAspect in applyLimits().
*/
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
/**
* Applies a coordinate transformation to the graphics context, to map
* xleft,xright,ytop,ybottom to the edges of the canvas. This is called
* by draw(). This does not need to be changed.
*/
//pcoords[0][0] =
//pcoords[0][1]=
function applyLimits(g, xleft, xright, ytop, ybottom, preserveAspect) {
var width = canvas.width; // The width of this drawing area, in pixels.
var height = canvas.height; // The height of this drawing area, in pixels.
var k = portals.length;
var j;
var i = 0, widthheight, myradius;
var localerror = false;
if (pcoords.length < k) {
while (portals[i]){
j = i + 1;
if (width > 100){
var rWidth = width/(k + 1);
rWidth= Math.floor(rWidth);
} else {
var lWidth = 0;
var rWidth = 0;
}
if (height > 100){
var bHeight = height/(k + 1);
bHeight= Math.floor(bHeight);
} else {
var tHeight = 0;
var bHeight = 0;
}
var myleft = leftj[i] * rWidth - 50;
var mytop = j * bHeight - 50;
pcoords[i]= new Array;
pcoords[i][0] = myleft;
pcoords[i][1] = mytop;
i = i + 1;
}
}
if (preserveAspect) {
// Adjust the limits to match the aspect ratio of the drawing area.
var displayAspect = Math.abs(height / width);
var requestedAspect = Math.abs(( ybottom-ytop ) / ( xright-xleft ));
var excess;
if (displayAspect > requestedAspect) {
excess = (ybottom-ytop) * (displayAspect/requestedAspect - 1);
ybottom += excess/2;
ytop -= excess/2;
}
else if (displayAspect < requestedAspect) {
excess = (xright-xleft) * (requestedAspect/displayAspect - 1);
xright += excess/2;
xleft -= excess/2;
}
}
var pixelWidth = Math.abs(( xright - xleft ) / width);
var pixelHeight = Math.abs(( ybottom - ytop ) / height);
pixelSize = Math.min(pixelWidth,pixelHeight);
if (frameNumber == 4 || frameNumber == 5){
pcoords.push([(5.25-xleft)*width / (xright-xleft),(3.3-ytop)*height / (ybottom-ytop)]);
pcoords.push([(-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*width / (xright-xleft), (0.25-ytop)*height / (ybottom-ytop)]);
}
g.scale( width / (xright-xleft), height / (ybottom-ytop) );
g.translate( -xleft, -ytop );
// if (frameNumber < 3)
}
//------------------ Animation framework ------------------------------
var animationTimeout = null; // A null value means the animation is off.
// Otherwise, this is the timeout ID.
function frame() {
// Draw one frame of the animation, and schedule the next frame.
updateFrame();
draw();
canvas.addEventListener("mousedown", doMouseDown, false);
animationTimeout = setTimeout(frame, 33);
}
function setAnimationRunning(run) {
if ( run ) {
if (animationTimeout == null) {
// If the animation is not already running, start
// it by scheduling a call to frame().
animationTimeout = setTimeout(frame, 33);
}
}
else {
if (animationTimeout != null) {
// If the animation is running, stop it by
// canceling the next scheduled call to frame().
clearTimeout(animationTimeout);
}
animationTimeout = null; // Indicates that animation is off.
}
}
//----------------------- initialization -------------------------------
function init() {
try {
canvas = document.getElementById("theCanvas");
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
graphics = canvas.getContext("2d");
}
catch (e) {
document.getElementById("message").innerHTML =
"Sorry, this page requires canvas graphics, but<br>" +
"it looks like your browser does not support it<br>" +
"Reported error: " + e;
return;
}
// add any other necessary initialization
document.getElementById("animateCheck").checked = true; // Make sure box is checked!
loadImage("dog-walking11");
createWorld();
setAnimationRunning(true); // start the animation
}
</script>
</head>
<body onload="init()" style="background-color: rgb(220,220,220)">
<div id="messagediv">
<h2>Hierarchical Modeling Example</h2>
<!-- For error reporting: the contents of the noscript tag are
shown only if JavaScript is not available. The paragraph with
id="message" is for reporting errors using JavaScript.-->
<noscript><b>This page requires JavaScript, which is<br>
not enabled in your browser.</b></noscript>
<p id="message" style="color:red"></p>
<p><input type="checkbox" id="animateCheck" onchange="setAnimationRunning(this.checked)">
<label for="animateCheck">Run Animation</label>
</p>
</div>
<div id="canvasdiv">
<canvas id="theCanvas" width= "400" height= "300"
style="background-color: transparent"></canvas>
</div>
</body>
</html>
It seems that I had to use graphics.drawImage() after using graphics.restore().
Though I was trying to draw the image in the correct order (after), compared to drawing the rectangles, circles etc, the shapes did not come out of the buffer onto the page until after the restore function().
When I was calling drawImage, I assumed that it was loading it onto the buffered canvas with the rest of the things, ready to be drawn in the correct order, when in fact it was putting it straight onto the page.
It seems strange since it was being called with the graphics context, so I thought it would be added to the rest of the canvas graphics, but i appear to be wrong.

Javascript accessing dynamic object properties

I am a novice programmer working with OpenJScad written in Javascript to build 3D models.
I am trying to figure out how to structure my code so that I can access an object's instance properties that are dynamically created with user input parameters. I have a parent Gear class with the following variable...
// Gear parent class
Gear = function(numTeeth, circularPitch, pressureAngle, clearance, thickness)
{
var pitchRadius = numTeeth * circularPitch / (2 * Math.PI);
I am making several Gear sub-classes that accept user parameters, ie...
// Spur Gear
function makeSpur(params)
{
var gear = new Gear(
params.spurTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.inputBore
);
if(params.inputBore > 0)
{
var inputBore = CSG.cylinder({start: [0,0,-params.thickness2], end:
[0,0,params.thickness2], radius: params.inputBore, resolution: 16});
gear = gear.subtract(inputBore).rotateX(90);
}
return gear;
...and then dynamically generating location coordinates based on the pitchRadius property of another Gear object...
// function main
var spurGear = makeSpur(params);
spurGear = spurGear.translate([-pinionGear.pitchRadius,0,0]);
Everything renders, except when I try to access the pitchRadius property from another Gear instance. Ive read about prototypes and accessing private / public properties, but I just can't figure out how to structure the code so that I can access instance properties in function main.
Here is the full code...
include("gears.jscad");
// Here we define the user editable parameters:
function getParameterDefinitions() {
return [
{ name: 'circularPitch', caption: 'Circular pitch:', type: 'float', initial: 5 },
{ name: 'pressureAngle', caption: 'Pressure angle:', type: 'float', initial: 20 },
{ name: 'clearance', caption: 'Clearance:', type: 'float', initial: 0 },
{ name: 'thickness', caption: 'Thickness of transmission gears:', type: 'float', initial: 5 },
{ name: 'spurTeeth', caption: 'Number of spur teeth:', type: 'int', initial: 32 },
{ name: 'pinionTeeth', caption: 'Number of pinion teeth:', type: 'int', initial: 14 },
{ name: 'bore', caption: 'Radius of shaft:', type: 'float', initial: 5 }
];
}
// Main function
function main(params)
{
// declare parts
spurGear = new makeSpur(params);
pinionGear = new makePinion(params);
// assemble parts
spurGear = spurGear.translate([-pinionGear.pitchRadius, 0, -20]); // BREAKS CODE
pinionGear = pinionGear.translate([-spurGear.pitchRadius, 0, 20]); // BREAKS CODE
return [spurGear,pinionGear];
}
// Spur Gear
function makeSpur(params)
{
var gear = new involuteGear(
params.spurTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.thickness,
params.bore
);
if(params.bore > 0)
{
var bore = CSG.cylinder({start: [0,0,-params.thickness], end: [0,0,params.thickness], radius: params.bore, resolution: 16});
gear = gear.subtract(bore).rotateX(90);
}
return gear;
}
// Pinion Gear
function makePinion(params)
{
var gear = new involuteGear(
params.pinionTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.thickness,
params.bore
);
if(params.bore > 0)
{
var bore = CSG.cylinder({start: [0,0,-params.thickness], end: [0,0,params.thickness], radius: params.bore, resolution: 16});
gear = gear.subtract(bore).rotateX(90);
}
return gear;
}
// title: Gear
// author: Joost Nieuwenhuijse
// license: MIT License
/*
For gear terminology see:
http://www.astronomiainumbria.org/advanced_internet_files/meccanica/easyweb.easynet.co.uk/_chrish/geardata.htm
Algorithm based on:
http://www.cartertools.com/involute.html
circularPitch: The distance between adjacent teeth measured at the pitch circle
*/
function involuteGear(numTeeth, circularPitch, pressureAngle, clearance, thickness)
{
// default values:
if(arguments.length < 3) pressureAngle = 20;
if(arguments.length < 4) clearance = 0;
if(arguments.length < 4) thickness = 1;
var addendum = circularPitch / Math.PI;
var dedendum = addendum + clearance;
// radiuses of the 4 circles:
this.pitchRadius = numTeeth * circularPitch / (2 * Math.PI);
// this.getpitchRadius = function() {
//return pitchRadius;
//};
var baseRadius = this.pitchRadius * Math.cos(Math.PI * pressureAngle / 180);
var outerRadius = this.pitchRadius + addendum;
var rootRadius = this.pitchRadius - dedendum;
var maxtanlength = Math.sqrt(outerRadius*outerRadius - baseRadius*baseRadius);
var maxangle = maxtanlength / baseRadius;
var tl_at_pitchcircle = Math.sqrt(this.pitchRadius*this.pitchRadius - baseRadius*baseRadius);
var angle_at_pitchcircle = tl_at_pitchcircle / baseRadius;
var diffangle = angle_at_pitchcircle - Math.atan(angle_at_pitchcircle);
var angularToothWidthAtBase = Math.PI / numTeeth + 2*diffangle;
// build a single 2d tooth in the 'points' array:
var resolution = 5;
var points = [new CSG.Vector2D(0,0)];
for(var i = 0; i <= resolution; i++)
{
// first side of the tooth:
var angle = maxangle * i / resolution;
var tanlength = angle * baseRadius;
var radvector = CSG.Vector2D.fromAngle(angle);
var tanvector = radvector.normal();
var p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
points[i+1] = p;
// opposite side of the tooth:
radvector = CSG.Vector2D.fromAngle(angularToothWidthAtBase - angle);
tanvector = radvector.normal().negated();
p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
points[2 * resolution + 2 - i] = p;
}
// create the polygon and extrude into 3D:
var tooth3d = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
var allteeth = new CSG();
for(var j = 0; j < numTeeth; j++)
{
var ang = j*360/numTeeth;
var rotatedtooth = tooth3d.rotateZ(ang);
allteeth = allteeth.unionForNonIntersecting(rotatedtooth);
}
// build the root circle:
points = [];
var toothAngle = 2 * Math.PI / numTeeth;
var toothCenterAngle = 0.5 * angularToothWidthAtBase;
for(var k = 0; k < numTeeth; k++)
{
var angl = toothCenterAngle + k * toothAngle;
var p1 = CSG.Vector2D.fromAngle(angl).times(rootRadius);
points.push(p1);
}
// create the polygon and extrude into 3D:
var rootcircle = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
var result = rootcircle.union(allteeth);
// center at origin:
result = result.translate([0, 0, -thickness/2]);
return result;
}
I noticed that you are actually returning CSG object in the constructor, so try to use properties container described in OpenJSCAD User guide. According to the guide properties variable is intended to store metadata for the object.
This is an example from guide:
var cube = CSG.cube({radius: 1.0});
cube.properties.aCorner = new CSG.Vector3D([1, 1, 1]);
Additional comments:
You are returning different object then this in your constructor
If you will do something like this: gear = gear.rotateX(90); then you have new object
If you will use properties then metadata is cloned when you do transformation.

Categories