Is it possible to create a Cesium.Ray from a Cesium.Cartesian3 and a Cesium.HeadingPitchRoll?
If so, how would one go about this?
EDIT 1:
const hpr = Cesium.HeadingPitchRoll.fromDegrees(0, 0, 0)
const matrix3 = Cesium.Matrix3.fromHeadingPitchRoll(hpr)
// The UNIT_X vector is "forward" in Cesium. We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
const direction = Cesium.Matrix3.multiplyByVector(
matrix3,
Cesium.Cartesian3.UNIT_X,
new Cesium.Cartesian3()
)
const origin = Cesium.Cartesian3.fromDegrees(i.longitude, i.latitude, i.altitude)
// Now we can construct the Ray.
const ray = new Cesium.Ray(origin, direction)
const finalPoint = Cesium.Ray.getPoint(ray, i.altitude)
const e = map.viewer.entities.add({
name: i.id,
polyline: {
positions: [origin, finalPoint],
width: 4,
arcType: Cesium.ArcType.NONE,
material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED),
},
})
With HPR set to (0,0,0), this results in:
With a pitch of 0, I would be expecting the ray to be parallel with the horizon instead of what seems to be +45 degrees.
Yes, it is possible. These two kinds of objects hold different kinds of information, so be careful that you're not losing data in the conversion.
A Ray has an origin (Cartesian position) and a direction which is just a unit vector, not the same as a full orientation.
The HeadingPitchRoll is a full orientation. The Heading and Pitch work together to identify a particular "forward" direction, and Roll indicates a rotation about that direction vector. This Roll rotation does not affect the direction, and so will not have an effect on a Ray.
Here's the code:
// A Cesium Ray consists of an "origin" and a "direction".
// The origin is just a Cartesian3 from anywhere.
var origin = new Cesium.Cartesian3(1.0, 2.0, 3.0);
// For this question, "direction" comes from a HeadingPitchRoll.
// Let's establish an HPR here.
var heading = Cesium.Math.toRadians(0); // YOUR VALUE HERE
var pitch = Cesium.Math.toRadians(0); // YOUR VALUE HERE
var roll = Cesium.Math.toRadians(0); // This one does nothing.
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
// Next we need a direction vector from the HPR. We'll use
// a rotation matrix (3x3) to do the conversion.
var matrix3 = Cesium.Matrix3.fromHeadingPitchRoll(hpr);
// The UNIT_X vector is "forward" in Cesium. We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
var direction = Cesium.Matrix3.multiplyByVector(matrix3, Cesium.Cartesian3.UNIT_X,
new Cesium.Cartesian3());
// Now we can construct the Ray.
var ray = new Cesium.Ray(origin, direction);
console.log('Ray: ' + JSON.stringify(ray));
If you run this with the default zero heading/pitch shown here, you'll get the unit X vector back again:
Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":1,"y":0,"z":0}}
But if you set the pitch to 45 degress:
var heading = Cesium.Math.toRadians(0);
var pitch = Cesium.Math.toRadians(45);
You'll see the X direction has pitched up towards +Z.
Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":0.7071067811865476,"y":0,"z":0.7071067811865475}}
A positive heading will rotate the X vector towards -Y (clockwise as seen from +Z above).
var heading = Cesium.Math.toRadians(45);
var pitch = Cesium.Math.toRadians(0);
Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":0.7071067811865476,"y":-0.7071067811865475,"z":0}}
EDIT: The question was revised a bit. The Ray's direction needs to be in Earth-Centered Fixed frame, not local-up frame, for the intended use.
Here's a longer demo that shows how to handle this. You can also view a live demo on Sandcastle.
var viewer = new Cesium.Viewer("cesiumContainer");
const i = {
id: 'test',
longitude: -79,
latitude: 40,
altitude: 500
};
// Create the origin and hpr.
const origin = Cesium.Cartesian3.fromDegrees(i.longitude, i.latitude, i.altitude)
let hpr = Cesium.HeadingPitchRoll.fromDegrees(0, 0, 0)
// Cesium uses East-North-Up axes, where East is zero. If you want North == Zero,
// you must subtract 90 degrees' worth of radians (pi/2).
hpr.heading -= Cesium.Math.PI_OVER_TWO
// We'll get the orientation, which is a quaternion. This is specific to the
// location on the planet. Also it comes in handy later for the 3D model vis.
const orientation = Cesium.Transforms.headingPitchRollQuaternion(origin, hpr);
// Get a Matrix3 version of that Quaternion.
const matrix3 = Cesium.Matrix3.fromQuaternion(orientation);
// The UNIT_X vector is "forward" in Cesium. We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
const direction = Cesium.Matrix3.multiplyByVector(
matrix3,
Cesium.Cartesian3.UNIT_X,
new Cesium.Cartesian3()
)
// Now we can construct the Ray.
const ray = new Cesium.Ray(origin, direction)
const finalPoint = Cesium.Ray.getPoint(ray, i.altitude)
const e = viewer.entities.add({
name: i.id,
polyline: {
positions: [origin, finalPoint],
width: 4,
arcType: Cesium.ArcType.NONE,
material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED),
},
})
// Optionally, show an aircraft model with this HPR.
const m = viewer.entities.add({
position: origin,
orientation: orientation,
model: {
uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
minimumPixelSize: 64,
}
})
viewer.zoomTo(e)
Related
We are using forge viewer(v7) in our web application.
Our requirement is to crop particular room/area from the forge viewer. For example, if we have shown a house model in the forge viewer then if a user select a kitchen(from menu or navbar) then the viewer should show only kitchen area (including all its objects like cabinets, burner, fridge, sink etc.) and all other objects/sections should be hidden. Similarly for bedrooms, baths etc. It will be just for viewing purpose at run time and not for any automation.
We are getting room coordinates(min and max X, Y, Z) with the help of following using forge API(with Revit engine).
GeometryElement geoElement = room.ClosedShell;
BoundingBoxXYZ boundingBox = geoElement.GetBoundingBox();
XYZ min = boundingBox.Min;
XYZ max = boundingBox.Max;
We are using viewer.setCutPlanes function to draw cutplanes in viewer.
var minPt = new THREE.Vector3(x,y,z); //!<<< put your point here
var maxPt = new THREE.Vector3(x,y,z); //!<<< put your point here
const normals = [
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(0, 0, -1)
];
const bbox = new THREE.Box3(minPt, maxPt);
const cutPlanes = [];
for (let i = 0; i < normals.length; i++) {
const plane = new THREE.Plane(normals[i], -1 * maxPt.dot(normals[i]));
// offset plane with negative normal to form an octant
if (i > 2) {
const ptMax = plane.orthoPoint(bbox.max);
const ptMin = plane.orthoPoint(bbox.min);
const size = new THREE.Vector3().subVectors(ptMax, ptMin);
plane.constant -= size.length();
}
const n = new THREE.Vector4(plane.normal.x, plane.normal.y, plane.normal.z, plane.constant);
cutPlanes.push(n);
}
viewer.setCutPlanes(cutPlanes);
But when we are passing these coordinates (obtained for API) to this front end JS code the cutPlanes are getting created at incorrect position/points. For example when we are passing coordinates of kitchen its cropping the small portion of roof and same with all other room.
The possible reason is that the Revit & forge viewer coordinates are not same.
Does anyone have an idea that how can we map these Revit coordinates with forge viewer and draw cutplanes?
If you're following the Forge Viewer tutorial to load the model, then you need to subtract global offsets from endpoints of the room bounding box like below:
var minPt = new THREE.Vector3(x,y,z); //!<<< put your point here
var maxPt = new THREE.Vector3(x,y,z); //!<<< put your point here
var offsetMatrix = viewer.model.getData().placementWithOffset;
var offsetMinPt = minPt.applyMatrix4(offsetMatrix);
var offsetMaxPt = maxPt.applyMatrix4(offsetMatrix);
i have another solution. modify model by hand like cut plane, hide, isolate element to retrieve view you want to show. Then use method var data = viewer.getState() and store that data to your database. then use viewer.restoreState(data) to recall your view.
I'm trying to add a "shadow" of the in my replay but I'm not exactly sure if I should name that as a "shadow".
Please, look at my screenshot, you can see a red shadow below every entities (where I set the dots to highlight what I'm looking for)
You can see it live on https://ayvri.com/scene/z15y96gzjx/ck43xpxd500013e5ra7dh6s8e
I did try to find something documented for this feature, but so far, I didn't found. Neither on the Sandcastle.
I thought I would need to add particles to add a kind of tail, but I'm not even sure if it's the right direction to investigate on.
Thanks for any help!
Edit 2 : To see clearly what I need to reproduce, you can also check this screenshot. Below the white trace, you can see a red gradient (from opacity 0 to 1) starting from the beginning of the trace.
there is working example with ground tracer as I promised:
var viewer = new Cesium.Viewer('cesiumContainer', {
scene3DOnly : true,
shadows : true,
timeline: false,
terrainShadows : Cesium.ShadowMode.ENABLED
});
var terrainProvider = Cesium.createWorldTerrain();
viewer.terrainProvider = terrainProvider;
//---------------------- Start position of the plane ---------------------------
var startLongitude = -123.0744619, startLatitude = 44.0503706;
viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 900)});
//--------------------------- CREATE A PLANE ENTITY ----------------------------
var plane = viewer.entities.add({
// position: new Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 600),
model : {
uri : '../SampleData/models/CesiumBalloon/CesiumBalloon.glb',
minimumPixelSize : 128,
maximumScale : 20000,
color : Cesium.Color.BLACK.withAlpha(0.5),
}
});
//------------ Array of (time, position) samples along the plane route ---------
var airPathArray = [];
//------------ Array of the points of the ground path under the plane ----------
var groundPathArray = [];
//------------ Initialize arrays ----------
var timeNow = new Cesium.JulianDate.now();
airPathArray.push({time: timeNow, degrees: {longitude: startLongitude, latitude: startLatitude},
position: new Cesium.Cartesian3.fromDegrees(startLongitude, startLatitude, 600)});
groundPathArray.push(startLongitude);
groundPathArray.push(startLatitude);
//----------- Every 1 second a new point on the plane route is created ---------
var pathHandler = setInterval(function(){
var timeNow = new Cesium.JulianDate.now();
//timeNow = Cesium.JulianDate.addSeconds(timeNow, 2, new Cesium.JulianDate());
// At start it takes initial coordinates
// New next point coordinates are randomly taken
var lon = airPathArray[airPathArray.length - 1].degrees.longitude + 0.0005 * (1 - Math.random());
var lat = airPathArray[airPathArray.length - 1].degrees.latitude + 0.0005 * (1 - Math.random());
airPathArray.push({time: timeNow, degrees: {longitude: lon, latitude: lat}, position: new Cesium.Cartesian3.fromDegrees(lon, lat, 600)});
groundPathArray.push(lon, lat);
// The maximum number of the route points is fixed
if(airPathArray.length > 3){
airPathArray.shift();
groundPathArray.shift();
groundPathArray.shift();
}
// The updated route is ready for visualization
viewRoute();
}, 1000);
// Route visualization
function viewRoute() {
// console.log(airPathArray[0].position);
var trace = new Cesium.SampledPositionProperty();
for(var i = 0; i < airPathArray.length; i++) {
trace.addSample(airPathArray[i].time, airPathArray[i].position);
}
plane.position = airPathArray[0].position;
//plane.position.setInterpolationOptions({
// interpolationDegree : 1,
// interpolationAlgorithm : Cesium.LinearApproximation
//});
plane.orientation = new Cesium.VelocityOrientationProperty(trace);
}
//------------------------ CREATE A GROUND PATH ENTITY -------------------------
function updatePositions() {
var positions = new Cesium.Cartesian3.fromDegreesArray(groundPathArray);
return positions;
}
var goroundPath = viewer.entities.add({
corridor : {
width : 50.0,
material : Cesium.Color.BLUE.withAlpha(0.5),
positions: new Cesium.CallbackProperty(updatePositions, false)
}
});
I'm trying to control the camera in the Autodesk Forge Viewer. Setting target and position seems to work fine, but if I try to set rotation or quaternion it do not have any effect.
To get the camera I use the getCamera function and then applyCamera after I have tried to set the parameters.
What I'm trying to achieve is to use the device orientation on a handheld device to rotate the model. Just using alpha and beta to set target is not a smooth experience.
// get camera
var cam = _viewer.getCamera();
// get position
var vecPos = cam.position;
// get view vector
var vecViewDir = new THREE.Vector3();
vecViewDir.subVectors(cam.target,cam.position);
// get length of view vector
var length = vecViewDir.length();
// rotate alpha
var vec = new THREE.Vector3();
vec.y = length;
var zAxis = new THREE.Vector3(0,0,1);
vec.applyAxisAngle(zAxis,THREE.Math.degToRad(alpha));
// rotate beta
var vec2 = new THREE.Vector3(vec.x,vec.y,vec.z);
vec2.normalize();
vec2.negate();
vec2.cross(zAxis);
vec.applyAxisAngle(vec2,THREE.Math.degToRad(beta) + Math.PI / 2);
// add to camera
cam.target.addVectors(vecPos,vec);
_viewer.applyCamera(cam,false);
You need to use the setView() method
_viewer.navigation.setView (pos, target) ;
and may also need to set the up vector to make sure you orient the camera the way you want.
_viewer.navigation.setCameraUpVector (upVector) ;
I am trying to find out the LatLng based on my x,y pixel coordinates (and of course map options, such as zoom and center).
In order to do so, I posted another question and someone came up with this solution, from this post:
/**
* #param {google.maps.Map} map
* #param {google.maps.Point} point
* #param {int} z
* #return {google.maps.LatLng}
*/
var pointToLatlng = function(map, point, z){
var scale = Math.pow(2, z);
var normalizedPoint = new google.maps.Point(point.x / scale, point.y / scale);
var latlng = map.getProjection().fromPointToLatLng(normalizedPoint);
return latlng;
};
As you can notice from the code sample, the function uses as argument a google.maps.Point, therefore I need to convert my screen pixel coordinate into a google.maps.Point and I have no clue how, since their documentation of the API is not quite verbose...
Can you please help me? Or am I missing something on the way?
After some research and some fails I came up with a solution.
Following the documentation from this link I found out that the google Points are computed in the range of x:[0-256], y:[0-256] (a tile being 256x256 pixels) and the (0,0) point being the leftmost point of the map (check the link for more information).
However, my approach is as it follows:
having the x and y coordinates (which are coordinates on the screen - on the map) I computed the percentage where the x and y coordinates were placed in response to the div containing the map (in my case, the hole window)
computed the NortEast and SouthWest LatLng bounds of the (visible) map
converted the bounds in google Points
computed the new lat and lng, in google points, with the help of the boundaries and percentage of x and y
// retrieve the lat lng for the far extremities of the (visible) map
var latLngBounds = map.getBounds();
var neBound = latLngBounds.getNorthEast();
var swBound = latLngBounds.getSouthWest();
// convert the bounds in pixels
var neBoundInPx = map.getProjection().fromLatLngToPoint(neBound);
var swBoundInPx = map.getProjection().fromLatLngToPoint(swBound);
// compute the percent of x and y coordinates related to the div containing the map; in my case the screen
var procX = x/window.innerWidth;
var procY = y/window.innerHeight;
// compute new coordinates in pixels for lat and lng;
// for lng : subtract from the right edge of the container the left edge,
// multiply it by the percentage where the x coordinate was on the screen
// related to the container in which the map is placed and add back the left boundary
// you should now have the Lng coordinate in pixels
// do the same for lat
var newLngInPx = (neBoundInPx.x - swBoundInPx.x) * procX + swBoundInPx.x;
var newLatInPx = (swBoundInPx.y - neBoundInPx.y) * procY + neBoundInPx.y;
var finalResult = new google.maps.Point(newLngInPx, newLatInPx);
You could use an overlay that draws nothing in order to get at the super valuable function, fromContainerPixelToLatLng()
var overlay = new google.maps.OverlayView();
overlay.draw = function() {}; // empty function required
overlay.setMap(map);
var coordinates = overlay.getProjection().fromContainerPixelToLatLng(
new google.maps.Point(x, y)
);
console.log(coordinates.lat + ", " + coordinates.lng);
Firstly, I got a 'maximum call stack size exceeded' error on some JavaScript code I'll be posting below, and I'm not sure where it's coming from. I don't think I'm calling anything recursively, and I couldn't find any other cause of the error from looking around. I'm using Chrome to execute the program, and the only details I can get out of the console are 'Vector Vector Vector Vector Vector Vector Vector...' etc. I think this means that the Vector function is recursively calling itself, but, again, I can't figure out where that's happening. So, firstly, here's the Vector function:
function Vector(top,left) { //a vector constructor, with all necessary vector operations
this.top = top; //the 'top' component, which points down
this.left = left; //the 'left' component, which points right
this.in = 0; //the 'in' component, which points out of the screen
this.magnitude = Math.sqrt(this.top*this.top+this.left*this.left); //the magnitude of the vector
this.reverse = new Vector(-this.top,-this.left); //the vector that points opposite of this vector
this.unit = this.Scale(1/this.magnitude); //the unit vector pointing in this vector's direction
this.Scale = Scale; //multiplying vectors by a constant
this.Add = Add; //easier vector addition
this.Subtract = Subtract; //and subtraction
this.Dot = Dot; //a dot product operation
this.Cross = Cross; //the cross product of two planar vectors; it returns the 'in' component of the resulting vector
this.Project = Project; //the projetion of the parameter vector onto this vector
function Scale(scalar) {return new Vector(scalar*this.top,scalar*this.left);}
function Add(vector) {return new Vector(this.top+vector.top,this.left+vector.left);}
function Subtract(vector) {return new Vector(this.top-vector.top,this.left-vector.left);} //the abovedescribed methods
function Dot(vector) {return this.top*vector.top+this.left*vector.left;}
function Cross(vector) {return this.top*vector.left-this.left*vector.top;}
function Project(vector) {return this.Scale(this.Dot(vector)/Math.pow(this.magnitude,2))}
}
var zeroVector = new Vector(0,0);
And here's the full code. Feel free to completely ignore it, though maybe something will be blatantly obvious to you which was impeccably hidden from me.
<!DOCTYPE html>
<html>
<head>
<title>temp</title>
</head>
<body>
<canvas
id="canvas"
height="768px"
width="1366px"
style="position:fixed;top:0px;left:0px;"
/>
</body>
<script>
/*------------------------------------------------------------Composition Center--------------------------------------------------*/ {
window.onload = run; //it'll start running as soon as it's started up
var interval; //the interval variable for the 'run' function
function run() {
interval = setInterval(function() {
for (i=0; i!=0; i++) {
var Point = point;
var vPrime = zeroVector;
var netForce = zeroVector;
for (j=0; j!=0; j++) {
var Edge = edge;
updateCollision(Point,Edge);
}
updatePosition(Point);
updateVelocity(Point);
draw();
}
},16);
}
/*---------------------------------------------------------------------------------------------------------------------------------*/ }
/*-------------------------------------------------------------Physics Engine------------------------------------------------------*/ {
//--Constructors--//
function PointObject(mass,top,left) { //a point object
this.mass = mass; //mass
this.position = new Vector(top,left); //position
this.velocity = zeroVector; //velocity
this.near = nullEdge; //a memory tool which stores the edge the point is close to
}
function Rectangle(top,left,height,width) { //a rectangle that'll be solid, but not affected by gravity or anything
this.position = new Vector(top,left);
this.height = height; //self-explanatory
this.width = width;
}
function Edge(start,end) {
this.start = start; //the start point (vector)
this.end = end; //end point (vector)
this.vector = end.Subtract(start); //the vector pointing from the start to the end point of the edge
this.RUN = new Vector(this.vector.left,-this.vector.top).unit; //the RIGHT pointing UNIT NORMAL vector to the edge vector
this.rightPermeable = false; //whether or not the edge can be passed through from the right to the left
this.leftPermeable = true; //or from the left to the right
}
//--Objects--//
var base = new Rectangle(3/4*768,33,768/4,1300); //the main base solid
var point = new PointObject(1,base.position.top-201,1366/2); //the point object at the center of this whole ordeal
var edge = new Edge(new Vector(base.position.top,base.position.left+base.width),base.position); //a test edge. they'll be auto-assembled later
var nullEdge = new Edge(zeroVector,zeroVector);
var deltaT = .01; //standard time interval
//--Loop o' Physics--//
//Update Collision
function updateCollision(Point,edge) { //check if any points are crossing edges and deal with that
var startToPoint = Point.position.Subtract(edge.start); //the vector from the start point of the edge to the tested point
if (edge.rightPermeable && edge.vector.Cross(startToPoint) < 0) {return "Negatory.";} //if it could only approach from a permeable side, stop testing
if (edge.leftPermeable && edge.vector.Cross(startToPoint) > 0) {return "Negatory.";}
var projection = edge.vector.Project(startToPoint); //the projection of that vector onto the edge vector
if (projection.Dot(edge.vector) < 0) {return "Negatory.";} //if it's not pointing in the same dircetion as the edge vector, it's not gonna hit the edge
var distance = startToPoint.Subtract(projection); //the normal vector to the edge that points to the Point
var velocity = distance.Project(Point.velocity); //the component of the point's velocity orthogonal to the edge
if (distance.Dot(velocity) >= 0) {return "Negatory.";} //if the orthogonal velocity component is pointing 'to the point,' it won't collide
var specificT = distance.magnitude/velocity.magnitude; //the time it'll take for the point to hit the edge, if it continues with it's current velocity
if (deltaT <= specificT) {return "Negatory.";} //if the point can't move far enough to cover the distance, it won't collide
//at this point, the collision will happen
Point.near = edge; //have the point remember that it collided with this edge, so it won't have to be totally re-tested for normal forces
Point.position = Point.position.Add(Point.velocity.Scale(specificT)).Add(distance.Scale(.0001)); //move the point just a wee bit away from the edge
vPrime = Point.velocity.Add(velocity.reverse); //impulse away the velocity orthogonal to the edge
Point.velocity = Point.velocity.Scale(0); //zero the current velocity
}
//Update Position
function updatePosition(Point) {Point.position = Point.position.Add(Point.velocity.Scale(deltaT));} //d=vt, essentially
//Update Velocity
function updateVelocity(Point) { //via forces
var gravity = new Vector(20,0); //gravity... hobviously
netForce = netForce.Add(gravity); //add up all the current forces to get the right normal force
if (Point.near != nullEdge) { //calculating normal force, so it needs to be near an edge
var startToPoint = Point.position.Subtract(Point.near.start);
var projection = Point.near.vector.Project(startToPoint); //same stuff as earlier
var distance = startToPoint.Subtract(projection);
if (distance.magnitude <= .01) { //if it's very near to the edge
normalForce = distance.Project(netForce.reverse); //exert enough force to cancel anything going into the edge
if (normalForce.Dot(distance) > 0) {netForce = netForce.Add(normalForce);} //if it is indeed going into the edge
} else if (distance.magnitude > 1) {Point.near = nullEdge;} //it's not near the edge any more
}
Point.velocity = Point.velocity.Add(netForce.Scale(deltaT/Point.mass)); //v=at, essentially
}
/*---------------------------------------------------------------------------------------------------------------------------------*/ }
/*----------------------------------------------------Graphics----------------------------------------------------------------------*/ {
var c = document.getElementById("canvas").getContext("2d"); //the means for drawing all this
PointObject.prototype.Draw = function() { //the draw method for point objects
c.fillStyle = "#000000"; //a black square with the point at its center pixel
c.fillRect(this.position.left-2,this.position.top-2,5,5);
}
Rectangle.prototype.Draw = function() { //draw method for rectangles
c.fillStyle = "#c0c0c0"; //a grey rectangle; the position is the top left corner
c.fillRect(this.position.left,this.position.top,this.width,this.height);
}
function draw() { //draws everything
c.clearRect(0,0,1366,768); //clear the screen
base.Draw(); //paint stuff
point.Draw();
}
/*----------------------------------------------------------------------------------------------------------------------------------*/ }
/*------------------------------------------------------------Math Tools-------------------------------------------------------------*/ {
//--Vector Tools--//
function Vector(top,left) { //a vector constructor, with all necessary vector operations
this.top = top; //the 'top' component, which points down
this.left = left; //the 'left' component, which points right
this.in = 0; //the 'in' component, which points out of the screen
this.magnitude = Math.sqrt(this.top*this.top+this.left*this.left); //the magnitude of the vector
this.reverse = new Vector(-this.top,-this.left); //the vector that points opposite of this vector
this.unit = this.Scale(1/this.magnitude); //the unit vector pointing in this vector's direction
this.Scale = Scale; //multiplying vectors by a constant
this.Add = Add; //easier vector addition
this.Subtract = Subtract; //and subtraction
this.Dot = Dot; //a dot product operation
this.Cross = Cross; //the cross product of two planar vectors; it returns the 'in' component of the resulting vector
this.Project = Project; //the projetion of the parameter vector onto this vector
function Scale(scalar) {return new Vector(scalar*this.top,scalar*this.left);}
function Add(vector) {return new Vector(this.top+vector.top,this.left+vector.left);}
function Subtract(vector) {return new Vector(this.top-vector.top,this.left-vector.left);} //the abovedescribed methods
function Dot(vector) {return this.top*vector.top+this.left*vector.left;}
function Cross(vector) {return this.top*vector.left-this.left*vector.top;}
function Project(vector) {return this.Scale(this.Dot(vector)/Math.pow(this.magnitude,2))}
}
var zeroVector = new Vector(0,0);
/*-----------------------------------------------------------------------------------------------------------------------------------*/ }
</script>
</html>
Anyway, second thing is about how I've written some bits of the Vector constructor, which may possibly somehow be causing my error. At various points I use 'new Vector()' in the properties and methods of the function, and I don't know if that's illegal or not. I think I've done it before without error, but I'm not sure.
Also, for the 'magnitude,' 'reverse,' and 'unit' properties, should I rephrase them as methods? I think the way they are they're being locked into the magnitude/reverse/unit vector of the vector as it is created, so if the top or left properties are changed, they'll return incorrect values. The only reason I haven't changed them yet is that they strike me more as properties than methods, just conceptually. It hasn't caused any problems yet or anything; just wondering.
If you want this on JSFiddle or something else like that, I'd be happy to oblige you.