I want to create an arc in an SceneView. People told me I have to use EllipticArcSegment, and it works in MapView, but I can use it in SceneView. When I use EllipticArcSegment in ScenView I get a line not an arc. See the picture.
public MainWindow()
{
InitializeComponent();
MySceneView.Scene = new Scene(BasemapStyle.ArcGISImageryStandard);
MyMapView.Map = new Map(BasemapStyle.ArcGISTopographic);
MapPoint _spain = new MapPoint(-4.00475, 40.637861, SpatialReferences.Wgs84);
Graphic _arc = CreateArc(_spain, 0.080, 90.0, 45.0);
Graphic _arc1 = CreateArc(_spain, 0.080, 90.0, 45.0);
_AiminginZone_GO.Graphics.Add(_arc);
_AiminginZone_GO1.Graphics.Add(_arc1);
MyMapView.GraphicsOverlays.Add(_AiminginZone_GO);
MySceneView.GraphicsOverlays.Add(_AiminginZone_GO1);
}
private Graphic CreateArc(MapPoint center, double radius, double arcAngle, double azimuth)
{
_AiminginZone_GO.Graphics.Clear();
_AiminginZone_GO1.Graphics.Clear();
// set up spatial references
SpatialReference wgs84 = SpatialReferences.Wgs84;
SpatialReference webMercator = SpatialReferences.WebMercator;
// make a point in lat/long (WGS84)
MapPoint centrePoint = new MapPoint(center.X, center.Y, 56, wgs84);
MapPoint centrePoint1 = new MapPoint(center.X+0.05, center.Y, 56, wgs84);
// project it to a mercator projection where the unit of measure is metres
MapPoint mercatorPoint = (MapPoint)GeometryEngine.Project(centrePoint, webMercator);
// create an arc segment which is 50Kilometers radius, starting at 90 degrees, the arc angle is 45 degrees clockwise
//EllipticArcSegment arcSegment = new EllipticArcSegment(centrePoint, mercatorPoint, (Math.PI / 180) * 90, true, true, (Math.PI / 180) * 90, (Math.PI / 180) * -45, wgs84);
EllipticArcSegment arcSegment = EllipticArcSegment.CreateCircularEllipticArc(mercatorPoint, 50000, (Math.PI / 180) * 90, (Math.PI / 180) * -45, webMercator);
// make a part with the segment
Part part = new Part(webMercator);
part.Add(arcSegment);
// create the line from the part
Polyline line = new Polyline(part);
// add it to a graphic and graphics overlay to allow us to visualise it
var polylineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Dash, System.Drawing.Color.Red, 3.0);
Graphic arcGraphic = new Graphic(line, polylineSymbol);
return arcGraphic;
}
enter image description here
There is a bug using EllipticArcSegment.CreateCircularEllipticArc, please see the link to have more informat.
enter link description here
Related
I'm trying to print an image in the center of the page but I can't come up with any idea.
System.Windows.Point printLocation = new System.Windows.Point(50,50);
printLocation.X = pageWidth - 50 / 2; 50 is the margin
imageViewer = ImagePrintAdapter.CreateImageFromBitmapImage(img,printLocation);
printerDialog.PrintVisual(imageViewer, "Identification");
This is the CreateImageFromBitmapImagemethod
public static System.Windows.Controls.Image CreateImageFromBitmapImage(BitmapImage imgSource, System.Windows.Point imgLocation)
{
System.Windows.Controls.Image imageViewer = new System.Windows.Controls.Image();
imageViewer.BeginInit();
imageViewer.Source = imgSource;
imageViewer.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
imageViewer.Arrange(new System.Windows.Rect(imgLocation, imageViewer.DesiredSize));
imageViewer.EndInit();
imageViewer.UpdateLayout();
return imageViewer;
}
If I set the printLocation.X to be the half of the pageWidth, shouldn't it start at the center ?
You may simply draw the image into a DrawingVisual and print it. The following simplified example assumes that the bitmap size is smaller than the printable area size:
ImageSource image = ...
var rect = new Rect(
(printDialog.PrintableAreaWidth - image.Width) / 2,
(printDialog.PrintableAreaHeight - image.Height) / 2,
image.Width, image.Height);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawImage(bitmap, rect);
}
printDialog.PrintVisual(visual, "");
Note that you may as well use any other size for the Rectangle, i.e. scale the printed image accordingly.
I am trying to find out apis in JFreeChart, that would allow me to build up a chart, where a long the X axis I would have categories and the Y axis interval values corresponding to times.
Y axis should represent time.
X axis category.
This is similar to a google calendar scenario, where in the X axis such as day of the week. And on the Y axis, you can write what you do along certain regions of time.
The following JFREE chart, represents what I would like to represent.
However, my Y axis is holding time as miliseconds and not as a time unit (e.g. seconds).
In the following image, i illustrate categories along the X that we can imagine they are something like an edge on a graph.
And on the Y axis, we have for example something time spend traveling it and then after traveling it time spent saying cultivating it.
The Y axis is compised by double values, and it would be preferable to be able to use an appropriate time axis. This seems to be problem when using category data sets.
Alreayd to make the chart bellow, many hacks had to be put in place, to make the stacks not being rendered from 0 - by having invisible data series.
Althgout it is definitely not obvious how to use JFreeChart to solve the problem posted above, based on the demo7 sample mentinoed in my comments I can now say that it is definitely possible to use the XYBarChart as an engine to render the bars at the right places for your problem, while having an axis that represents time and another axis that represents whatever you want such as categories.
The sample7 example uses a numberic axis and allocates intervals of the numeric axis as steps for rendering with of a bar.
In such a way, the problem can easily be adapted to my question above, and here is the adapted application that pseudo-implements the chart on my question in a proper way.
import java.awt.Color;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYIntervalSeries;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
public class XYBarChartDemo7VarianRepresentingStackOverFlowQuestion extends ApplicationFrame {
/**
* Constructs the demo application.
*
* #param title
* the frame title.
*/
public XYBarChartDemo7VarianRepresentingStackOverFlowQuestion(String title) {
super(title);
JPanel chartPanel = createDemoPanel();
chartPanel.setPreferredSize(new java.awt.Dimension(500, 300));
setContentPane(chartPanel);
}
private static JFreeChart createChart(IntervalXYDataset dataset) {
JFreeChart chart = ChartFactory.createXYBarChart("XYBarChartDemo7", "Date", true, "Y", dataset,
PlotOrientation.HORIZONTAL, true, false, false);
XYPlot plot = (XYPlot) chart.getPlot();
// The Y axis is turned into a date axis
plot.setRangeAxis(new DateAxis("Date"));
// The X axis is turned is turned into a label axis
SymbolAxis xAxis = new SymbolAxis("Series",
new String[] { "R107 => R101", "R101 => R5", "R5 => R15", "R15 => R16" });
xAxis.setGridBandsVisible(false);
plot.setDomainAxis(xAxis);
// Enables using Y interval
XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();
renderer.setUseYInterval(true);
plot.setRenderer(renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
ChartUtilities.applyCurrentTheme(chart);
return chart;
}
/**
* Creates a sample dataset.
*
* #return A dataset.
*/
private static IntervalXYDataset createDataset() {
// Time points to represnet
// Edge: "R107 => R101"
RegularTimePeriod d0 = new Day(12, 6, 2007);
RegularTimePeriod d1 = new Day(13, 6, 2007);
RegularTimePeriod d2 = new Day(14, 6, 2007);
// "R101 => R5"
RegularTimePeriod d3 = new Day(15, 6, 2007);
RegularTimePeriod d4 = new Day(16, 6, 2007);
RegularTimePeriod d5 = new Day(17, 6, 2007);
// "R5 => R15"
RegularTimePeriod d6 = new Day(18, 6, 2007);
RegularTimePeriod d7 = new Day(19, 6, 2007);
RegularTimePeriod d8 = new Day(20, 6, 2007);
// "R15 => R16"
RegularTimePeriod d9 = new Day(21, 6, 2007);
RegularTimePeriod d10 = new Day(22, 6, 2007);
RegularTimePeriod d11 = new Day(23, 6, 2007);
/// Next edge that is not part of the path
RegularTimePeriod d12 = new Day(24, 6, 2007);
// Create three interval series (each series has a different color)
XYIntervalSeriesCollection dataset = new XYIntervalSeriesCollection();
XYIntervalSeries s1 = new XYIntervalSeries("ProductiveTime");
XYIntervalSeries s2 = new XYIntervalSeries("WaitTime");
// Series 1 and series2 along edge1
addItem(s1, d0, d1, 0);
addItem(s2, d1, d3, 0);
// Series 1 and series2 along edge2
addItem(s1, d3, d4, 1);
addItem(s2, d4, d6, 1);
// Series 1 and series2 along edge3
addItem(s1, d6, d7, 2);
addItem(s2, d7, d9, 2);
// Series 1 and series2 along edge4
addItem(s1, d9, d10, 3);
addItem(s2, d10, d12, 3);
// puts in the data set the data series
dataset.addSeries(s1);
dataset.addSeries(s2);
return dataset;
}
private static void addItem(XYIntervalSeries s, RegularTimePeriod p0, RegularTimePeriod p1, int index) {
s.add(index,
// xLow x xHigh we see this on the Y because chart is horizontal
index - 0.45, index + 0.45, p0.getFirstMillisecond(),
// yLow yHigh (time interval) - we see this on the X because chart is set as horizontal
// NOTE: - notice how miliseconds are being used to make the xy interval and not
// actually a date object. But then on the date axis we shall have a proper DateAxis for rendering
// these miliseconds values
p0.getFirstMillisecond(), p1.getFirstMillisecond());
}
/**
* Creates a panel for the demo.
*
* #return A panel.
*/
public static JPanel createDemoPanel() {
return new ChartPanel(createChart(createDataset()));
}
/**
* Starting point for the demonstration application.
*
* #param args
* ignored.
*/
public static void main(String[] args) {
XYBarChartDemo7VarianRepresentingStackOverFlowQuestion demo = new XYBarChartDemo7VarianRepresentingStackOverFlowQuestion(
"JFreeChart : XYBarChartDemo7.java");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
And the image bellow illustrates how stepping away from the BarChart and category data sets, and moving into the XYBarChart and into IntervalXYDataSets, we can render bars where we want them.
Note if we plot the graph vertically, it will look exactly like the first chart at the question. With time axis on the Y and and the cateogories on the X.
So, another satisfied user.
I am creating a simple project using SceneKit and cannot get any shadows to appear in the Scene Editor or in the compiled application no matter what I try.
I have tried creating a simple box, placing it on a plane and adding a spot light. Code would be as follows:
// Create some properties
var scnView: SCNView!
var scnMasterScene: SCNScene!
var boxNode: SCNNode!
var cameraPerspNode: SCNNode!
var cameraOrthNode: SCNNode!
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
setupView()
createAndAddBoxScene()
setupCamera()
createAndAddSpotLight()
}
// Setup the view.
func setupView() {
scnView = self.sceneView as SCNView
// Set the scnView properties.
scnView.showsStatistics = true
scnView.autoenablesDefaultLighting = false
scnView.allowsCameraControl = true
// Create a Master scene.
scnMasterScene = SCNScene()
// Set the scene view's scene node to the master scene.
scnView.scene = scnMasterScene
}
// Setup the scene.
func createAndAddBoxScene() {
// Create a box of type SCNGeometry
let boxGeometry: SCNGeometry = SCNBox(width: 2000, height: 2000, length: 2000, chamferRadius: 100)
// Add a difuse colour to the box' first material.
boxGeometry.firstMaterial?.diffuse.contents = NSColor.redColor()
// Create a node of type SCNNode and attach the boxGeometry to it.
// Note: A node can only have 1 geometry object attached to it.
boxNode = SCNNode(geometry: boxGeometry)
// Add the new boxNode to the scene's root node.
scnMasterScene.rootNode.addChildNode(boxNode)
// Create a floor plane.
let floorGeometry: SCNGeometry = SCNPlane(width: 20000, height: 20000)
// Add a difuse colour to the floor's first material.
floorGeometry.firstMaterial?.diffuse.contents = NSColor.yellowColor()
// Create a floorPlaneNode and attach the floor plane to it.
let floorNode: SCNNode = SCNNode(geometry: floorGeometry)
// Tilt the floorPlaneNode in x.
let floorNodeTiltDegreesX: Double = -90
let floorNodeTiltRadiansX: Double = floorNodeTiltDegreesX * (π/180)
floorNode.rotation = SCNVector4Make(1, 0, 0, CGFloat(floorNodeTiltRadiansX))
// Add the floorPlaneNode to the master scene.
scnMasterScene.rootNode.addChildNode(floorNode)
}
// Create a camera, position it and add it to the scene.
func setupCamera() {
// Create a camera node which will be used to contain the camera.
cameraPerspNode = SCNNode()
// Create a new camera.
let cameraPersp: SCNCamera = SCNCamera()
// Set camera properties.
cameraPersp.name = "myPerspCamera"
cameraPersp.usesOrthographicProjection = false
cameraPersp.orthographicScale = 9
cameraPersp.xFov = 30
cameraPersp.zNear = 1
cameraPersp.zFar = 20000
// Assign the camera to the .camera property of the node.
cameraPerspNode.camera = cameraPersp
// Set the position and rotation of the camera node (NOT the camera).
cameraPerspNode.position = SCNVector3(x: 0, y: 4000, z: 6000)
let cameraPerspTiltDegrees: Double = -30
let cameraPerspTiltRadians: Double = cameraPerspTiltDegrees * (π/180)
cameraPerspNode.rotation = SCNVector4Make(1, 0, 0, CGFloat(cameraPerspTiltRadians))
// Add the new cameraNode to the scene's root.
scnMasterScene.rootNode.addChildNode(cameraPerspNode)
}
func createAndAddSpotLight() -> Void {
let spot = SCNLight()
spot.type = SCNLightTypeSpot
spot.castsShadow = true
spot.color = NSColor(hue: 1, saturation: 0, brightness: 0, alpha: 1)
spot.spotInnerAngle = 30
spot.spotOuterAngle = 60
let spotNode = SCNNode()
spotNode.light = spot
spotNode.position = SCNVector3(x: 0, y: 2000, z: 2000)
let lookAt = SCNLookAtConstraint(target: boxNode)
spotNode.constraints = [lookAt]
}
If I bring in a .dae filed add a spot light, or directional light, using the Scene Editor I can light the scene but there are no shadows when I set the Cast Shadows property in the Attributes Inspector.
Can anyone shine any light on my problem?
Thanks
The scene is large because the 3D model has been created at 1:1 and it depicts a large building. After much trial and error I finally found the solution - I changed the scale of the light to 10, 10, 10 and the shadows appeared.
the dimensions in your scene are huge (in SceneKit 1 unit = 1 meter). You'll want to change your light's zFar property (and probably zNear too) accordingly.
I am currently reading Real-World Functional Programming: With Examples in F# and C# by Tomas Petricek and Jon Skeet. I am a bit puzzled by a particular example in which we generate an application that displays a pie chart with some population statistics along with labels.
Now it is the part of drawing the label or rather setting the coordinates of the label that I am confused by. I hope that the author doesn't mind that I attach the excerpt here but it would be hard to get clarification on the code without showing it.
let centerX, centerY = 300.0, 200.0
let labelDistance = 150.0
let drawLabel (gr: Graphics) title startAngle angle =
let lblAngle = float(startAngle + angle / 2)
let ra = Math.PI * 2.0 * lblAngle / 360.0
let x = centerX + labelDistance * cos(ra)
let y = centerY + labelDistance * sin(ra)
let size = gr.MeasureString(title, fnt)
let rc = new PointF(float32(x) - size.Width / 2.0f,
float32(y) - size.Height / 2.0f)
gr.DrawString(title, fnt, Brushes.Black, new RectangleF(rc, size))
It seems that labelDistance and centerX, centerY define some standard "offset" from the center of the drawing surface and I'm guessing that the trigonometric functions define the angle of the label because if I omit those then all labels are placed on top of eachother in the bottom right corner. But I don't quite understand how this works. What exactly happens here?
Giving this a go by adding comments, not necessarily worked out in this order:
// startAngle is the angle in degrees of this segment, angle is the angle of
// the segment itself.
let drawLabel (gr: Graphics) title startAngle angle =
// So this is the angle of the centre of this segment.
let lblAngle = float(startAngle + angle / 2)
// And ra is the same angle, now in radians.
let ra = Math.PI * 2.0 * lblAngle / 360.0
// So these work out the position of the label in the usual
// way, using cosine(angle-in-radians) and then scaling for the X
// and using sine for the Y. Both relative to the centre of the
// circle.
let x = centerX + labelDistance * cos(ra)
let y = centerY + labelDistance * sin(ra)
// How long, in pixels, is the text?
let size = gr.MeasureString(title, fnt)
// Create an instance of the right data structure adjusting
// so the calculated point is the centre of the rectangle
// in which the text will be drawn.
let rc = new PointF(float32(x) - size.Width / 2.0f,
float32(y) - size.Height / 2.0f)
// And, thus, we can now draw the text.
gr.DrawString(title, fnt, Brushes.Black, new RectangleF(rc, size))
So, if this is the code I'm using to draw a circle on my canvas:
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = "black";
ctx.stroke();
... how could I get an array of coordinates of the points that make up this circle so I can save them in a database and load on canvas later using the context.moveTo() and context.lineTo() methods to connect the dots, drawing the same circle?
I guess I'm asking if it's possible to draw this kind of circle using not .arc() method but by connecting dots with lines, if I only know my center coordinates and circle radius (and of course the width of the line and color). This should enable me to save each dot coordinates in an array as I loop through.
#Octopus is on the right track:
var centerX=100;
var centerY=100;
var radius=40;
// an array to save your points
var points=[];
for(var degree=0;degree<360;degree++){
var radians = degree * Math.PI/180;
var x = centerX + radius * Math.cos(radians);
var y = centerY + radius * Math.sin(radians);
points.push({x:x,y:y});
}
Then you can draw the circle using the point objects in the points array:
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
ctx.fillStyle="skyblue";
ctx.fill();
ctx.strokeStyle="lightgray";
ctx.lineWidth=3;
ctx.stroke()
A suggestion, however...
Instead of saving all the points in the database, just save the centerX/Y and radius in the database.
Then you can use this same math to create the points and draw the circle.
You are asking for the formula for a circle which is:
radius*radius = (x-centerX)*(x-centerX) + (y-centerY)*(y-centerY)
Or if you want to generate n points do something like this:
for (i=0;i<n;i++) {
x[i] = centerX + radius* Math.cos( (i*2*Math.PI)/n );
y[i] = centerY + radius*-Math.sin( (i*2*Math.PI)/n );
}