I've tried everything I can find suggested elsewhere and I've also tried every permutation of the code you can see below and I just cannot crack this.
The plot itself is working great there isn't an issue there. I have a certain screen in my application that I want to draw an OxyPlot onto this view but rotate 90 degrees to suit the data better (for various reasons the application is currently locked to portrait).
The code in the view is:
private void CreatePlotChart()
{
var normalRect = new CGRect(0,0, View.Frame.Width, View.Frame.Height);
var rotatedRect = new CGRect(0, 0, View.Frame.Height, View.Frame.Width); // height and width swapped
var plot = new PlotView();
var radians = -90d.ToRadians();
plot.Transform = CGAffineTransform.MakeRotation((nfloat)radians);
// plot.Frame = normalRect;
plot.Model = ViewModel.Model;
// plot.InvalidatePlot(true); // no discernable effect
Add(plot);
// plot.Draw(rotatedRect); // context error
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(
plot.AtTopOf(View),
plot.AtLeftOf(View),
plot.WithSameHeight(View),
plot.WithSameWidth(View),
plot.AtBottomOf(View)
);
}
The code above results in a chart as shown below, I also get this exact same chart if I pass in the rotatedRect to the constructor new PlotView(rotatedRect):
If I remove the use of constraints and pass in the rotatedRect like this:
private void CreatePlotChart()
{
var normalRect = new CGRect(0,0, View.Frame.Width, View.Frame.Height);
var rotatedRect = new CGRect(0, 0, View.Frame.Height, View.Frame.Width); // height and width swapped
var plot = new PlotView(rotatedRect);
var radians = -90d.ToRadians();
plot.Transform = CGAffineTransform.MakeRotation((nfloat)radians);
// plot.Frame = normalRect;
plot.Model = ViewModel.Model;
// plot.InvalidatePlot(true); // no discernable effect
Add(plot);
// plot.Draw(rotatedRect); // context error
// View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
//View.AddConstraints(
// plot.AtTopOf(View),
// plot.AtLeftOf(View),
// plot.WithSameHeight(View),
// plot.WithSameWidth(View),
// plot.AtBottomOf(View)
// );
}
I get a lot closer to the desired effect, as can be seen below:
If I go another step and "reset" it's frame to the cached normalRect I get even closer, with this:
All of these attempts feel too hacky. What is the best way of achieving the chart manipulation I need and maintaining the use of constraints to make sure things are positioned properly?
Update
If after the first chart is drawn and I kick off another select of data, the exact same code rendered the chart exactly correctly:
This is also 100% repeatable so I think this might be a bug in OxyPlot or some odd side effect of the way I'm using it.
Related
I have points generated one by one, and when a new point is generated, I want to draw a line segment connecting with the previous point. Like this:
var x by remember { mutableStateOf( 0.0f)}
var y by remember { mutableStateOf( 0.5f)}
var pStart by remember { mutableStateOf(Offset(0f, 0.5f))}
Canvas(modifier = Modifier.fillMaxSize()) {
canvasWidth = size.width
canvasHeight = size.height
val pEnd = Offset(x * canvasWidth, (1-y) * canvasHeight)
val col = if (pEnd.y < pStart.y) Color.Green else Color.Red
drawLine(
start = pStart,
end = pEnd,
strokeWidth = 4f,
color = col
)
pStart = pEnd
}
But this only draws the segment in a flash and no segments stay on the screen.
I know I can save the points to a list and redraw all the segments whenever a new point is added. But I just hope to economize. Is it possible?
There's no practical other way. You COULD in fact, keep track of just two points, adding a whole new Canvas (all Transparent and Filling the maximum Size, stacked on top of one another), for each extra point that is added. This does seem a bit impractical, but maybe try it out and do some benchmarking to see which one checks out. This is the only OTHER way I could think of, where you do not have to store all the points and recompose every time a point is added, since all the other lines would technically be frozen in space.
In response to the somewhat (unreasonably) aggressive comment below, here's some sample code. I assume you have a stream of new points coming in so a LiveData object is assumed to be the source of that, which I shall be converting to a MutableState<T> for my use-case.
var latestPoint by liveData.collectAsState()
var recordedPoint by remember { mutableStateOf(latestPoint) }
var triggerDraw by remember { mutableStateOf(false) }
var canvasList = mutableStateListOf<#Composable () -> Unit>Canvas>() // Canvas is the Composable
if(triggerDraw){
canvasList.add(
Canvas(){
/* you have the recordedPoint, and latestPoint, simply draw a line here */
}
)
triggerDraw = false
}
LaunchedEffect(latestPoint){
triggerDraw = true
}
canvasList.forEach {
it() // Invoke the Composable here
}
Thanks Dad!
Is there an easy way to visualize a custom hit area shape?
As described here
https://konvajs.github.io/docs/events/Custom_Hit_Region.html
the hitFunc attribute can be set to a function that uses the supplied context to draw a custom hit area / region. Something like this:
var star = new Konva.Star({
...
hitFunc: function (context) {
context.beginPath()
context.arc(0, 0, this.getOuterRadius() + 10, 0, Math.PI * 2, true)
context.closePath()
context.fillStrokeShape(this)
}
})
For debugging purposes, I would like an easy way to toggle visual rendering of the shape (circle in this case), eg by filling it yellow.
Thanks :)
Currently, there is no public API for that. But you still can add hit canvas into the page somewhere and see how it looks:
const hitCanvas = layer.hitCanvas._canvas;
document.body.appendChild(hitCanvas);
// disable absolute position:
hitCanvas.style.position = '';
http://jsbin.com/mofocagupi/1/edit?js,output
Or you can add hit canvas on top of the stage and apply an opacity to make scene canvases visible:
const hitCanvas = layer.hitCanvas._canvas;
stage.getContainer().appendChild(hitCanvas);
hitCanvas.style.opacity = 0.5;
hitCanvas.style.pointerEvents = 'none';
http://jsbin.com/gelayolila/1/edit?js,output
I'm saving a PDF file with CGPDF under iOS 10. For this I load an existing PDF page, and write it to a new file with a context. While doing so, the rotation information gets lost and the resulting PDF file has all pages re-arragend at 0°.
let writeContext: CGContext = CGContext(finalPDFURL, mediaBox: nil, nil)!
// Loop through all pages
let page: CGPDFPage = ...
var mediaBox = page.getBoxRect(.mediaBox)
writeContext.beginPage(mediaBox: &mediaBox)
writeContext.drawPDFPage(page)
writeContext.endPage()
// Loop finished
writeContext.closePDF()
Then I came up with this code, which handles rotation just fine, but seems to draw the content with a slight offset. Using it with a PDF which has text or anything else close to the margins, results in cut-off content. Tried later also setting x, y, etc. on the pageInfo dict but I guess I misunderstood something here, see 2nd question below.
let page: CGPDFPage = ...
// Set the rotation
var pageDict = [String: Int32]()
pageDict["Rotate"] = CGFloat.init(page.rotationAngle)
writeContext.beginPDFPage(pageDict as CFDictionary?)
writeContext.drawPDFPage(page)
writeContext.endPDFPage()
So my questions,
1) How to use the first approach but with rotation support? Or the second one, but without cropping of content?
2) Where would I find a complete listing of all available pageInfo key-value pairs for this method? https://developer.apple.com/reference/coregraphics/cgcontext/1456578-beginpdfpage
Thanks!
Question is old, but hope following answer will be useful for someone in the future. It will preserve the rotation information of the original PDF page
How to use the first approach but with rotation support?
let writeContext: CGContext = CGContext(finalPDFURL, mediaBox: nil, nil)!
// Loop through all pages
let page: CGPDFPage = ...
var mediaBox = page.getBoxRect(.mediaBox)
writeContext.beginPage(mediaBox: &mediaBox)
let m = page.getDrawingTransform(.mediaBox, rect: mediaBox, rotate: 0, preserveAspectRatio: true)
// Following 3 lines makes the rotations so that the page look in the right direction
writeContext.translateBy(x: 0.0, y: mediaBox.size.height)
writeContext.scaleBy(x: 1, y: -1)
writeContext.concatenate(m)
writeContext.drawPDFPage(page)
writeContext.endPage()
// Loop finished
writeContext.closePDF()
I got feedback from a fellow iOS coder who suggested following principle:
These three steps should allow you to recreate the original page in the destination, while maintaining the page rotation field: (1) set the source page rotation in the destination page via the page dictionary, (2) set that rotation (or possibly the rotation * -1?) in the CGContext you’re drawing into, and finally (3) explicitly set the media box in the destination to be identical to the source (no rotation).
I'm trying to get the distance between two UIImageView on my screen, and for that I planned on substracting the center coordinates of the image 2 (movable) from the image 1 (not movable).
For that, I need to be able to get dynamically the coordinates of these images and I'm using something like this :
var centerBoardX = CGRectGetMidX(BlackBoard.frame)
var centerBoardY = CGRectGetMidY(BlackBoard.frame)
var centerRoundX = CGRectGetMidX(round1.frame)
var centerRoundY = CGRectGetMidY(round1.frame)
println(centerBoardX - centerRoundX)
println(centerBoardY - centerRoundY)
But even if I'm moving the image2 all over the screen I always get the same result based on the initial coordinates of both UIImageViews.
What's wrong ? Why do I get only the initial coordinates and not the new ones?
Thanks !
OK, so I found what was not right.
My UIImageViews are defined by frames, and I was getting the coordinates of the frame, which are always the same.
To get the correct coordinates, I needed to get the info from the superview of the object.
So the working code is
var centerBoardX = BlackBoard.superview?.center.x
var centerBoardY = BlackBoard.superview?.center.y
var centerRoundX = Round1.superview?.center.x
var centerRoundY = Round1.superview?.center.y
println(centerBoardX! - centerRoundX!)
println(centerBoardY! - centerRoundY!)
And now it's perfectly working !
I created a skin called customSliderTrack in the graphical editor of Adobe Flash CS5.5. This Slider is now in the "library" of the FLA file.
I can apply this skin with the following code:
var cls:Class = getDefinitionByName("CustomSliderTrack") as Class;
var tmpTrack:Sprite = new cls();
slider.setStyle("sliderTrackSkin",tmpTrack);
However due to the binary nature of the FLA file and lack of compatibility of different Versions of Adobe Flash I need to implement it all in Actionscript.
I understand that cls is a MovieClip object but I cant get the same results with new MovieClip(). I think this might be related to the dashed Lines in the graphical editor(I modified the default SliderTrack_skin). I havn't found out yet what they mean and how to replace them with Actionscript code.
setStyle automatically sets the track.height and track.width. In case of the track.height the slider.height attribute does not seem to have any effect. To work around this problem simply set the track.height to the best value.
To access the track extend the Slider class and override the configUI Function:
public class CustomSlider extends Slider
{
override protected function configUI():void
{
// Call configUI of Slider
super.configUI();
// The sprite that will contain the track
var t:Sprite = new Sprite();
// Draw the content into the sprite
t.graphics.beginFill(0x000000, 0.1);
t.graphics.drawRect(0, -15, width, 30);
t.graphics.endFill();
// Set the Sprite to be the one used by the Slider
this.setStyle("sliderTrackSkin",t);
// Reset the height to the value that it should be
track.height = 30;
}
}
Depending on the complexity of your track asset, you could accomplish this with the drawing API: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html
A very simple example would be:
var track:Sprite = new Sprite();
track.graphics.lineStyle(2, 0xcccccc);
track.graphics.beginFill(0x000000, 1);
track.graphics.drawRect(0, 0, 400, 20);
track.graphics.endFill();
track.scale9Grid = new Rectangle(2, 2, 396, 16);
slider.setStyle("sliderTrackSkin",track);
This creates a track that is just a black rectangle, 400x20 pixels in size. You can set the scale9grid in code to control how the skin scales. In the example above the rectangle's border wont scale, but the black rectangle inside will. Experimenting with the methods in the drawing API could be all you need.
If you need a more complex asset, I'd recommend loading an image and then passing that in to slider.setStyle.