[Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

[Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext

mark.b.ballard
The following generates a huge amount of points during display (as the
polygon representation is constructed), but it all gets clipped:

            win := ScheduledWindow new.
        win component: (graphics := CompositePart new).
        win openIn: (30@30 extent: 500@500).
        arc := EllipticalArc boundingBox: (356@61 corner: 3.5d8@3.5d8)
startAngle: 259 sweepAngle: -80.
        arc displayStrokedOn: win graphicsContext

Reply | Threaded
Open this post in threaded view
|

Re: [Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext

Nicolas Cellier-3
[hidden email] a écrit :

> The following generates a huge amount of points during display (as the
> polygon representation is constructed), but it all gets clipped:
>
>             win := ScheduledWindow new.
> win component: (graphics := CompositePart new).
> win openIn: (30@30 extent: 500@500).
> arc := EllipticalArc boundingBox: (356@61 corner: 3.5d8@3.5d8)
> startAngle: 259 sweepAngle: -80.
> arc displayStrokedOn: win graphicsContext
>
>

I already patched EllipticalArc in ObjectWorks long time ago
- there is no use to computePoint if we just need the bounds
- there are some inaccuracy in computePoints

Unfortunately #outlineIntersects: and #regionIntersects: also call
computePoints to handle clipping.
They would also need a new implementation for obvious economy reasons.

Then your code will invoke #displayArcBoundedBy:startAngle:sweepAngle:
and the primitive will fail i think due to large numbers (most screen
primitive work only with SmallIntegers).

This will try and round the point, fail again, revert to a
#displayGeneralArcBBoxOrigin:extent:startAngle:sweepAngle: that will
compute the points again and invoke primDisplayPolygon: that will fail,
round points, and fail again due to at least two limitations
- small integers
- limited number of points of underlying library (X11 limit is not so
high and you can query it, for Windows i do not know the limit and you
cannot query it AFAIK, for Mac, i do not know)

Obviously, it's a long way before you can display such an Object.

You'll have to solve intersection of clipping rectangle with the ellipse
by yourself (outline and/or region), convert to a polyline (or polygon)
and display the result. Not that simple since there might be multiple
intersections.

Nicolas




EllipticalArc>>computeBounds
        "02/03/93 @NC
        CORRIGE UN BUG SMALLTALK...
        pas la peine de calculer tous les points...
        Les points cardinaux sont #(est nord ouest sud)
        (angles = #(0 pi/2 pi 3*pi/2)
        1/ combien de points cardinaux sont sur l'arc ?
        2/ tient compte du quadrant de départ pour savoir ce qu'il faut calculer"

        | startModulo nCard startQuadrant left right top bottom a b cx cy
radStart radStop x1 y1 x2 y2 ordreDeCalcul aCalculer |
        startModulo := sweepAngle > 0
                                ifTrue: [startAngle - (startAngle // 90 * 90)]
                                ifFalse: [startAngle negated - (startAngle negated // 90 * 90)].
        nCard := sweepAngle abs + startModulo // 90.
        nCard = 4 ifTrue: [^boundingBox].
        startQuadrant := startAngle - (startAngle // 360 * 360) // 90.
        left := boundingBox left.
        right := boundingBox right.
        top := boundingBox top.
        bottom := boundingBox bottom.
        a := boundingBox width asFloat / 2.
        b := boundingBox height asFloat / 2.
        cx := boundingBox center x.
        cy := boundingBox center y.
        radStart := startAngle degreesToRadians.
        radStop := (startAngle + sweepAngle) degreesToRadians.
        x1 := (a * radStart cos + cx max: left) min: right.
        y1 := (b * radStart sin negated + cy max: top) min: bottom.
        x2 := (a * radStop cos + cx max: left) min: right.
        y2 := (b * radStop sin negated + cy max: top) min: bottom.
        ordreDeCalcul := sweepAngle > 0
                                ifTrue: [#(#top #left #bottom #right)]
                                ifFalse: [#(#right #bottom #left #top)].
        aCalculer := Set new: 8.
        nCard + 1 to: 4 do: [:i | aCalculer add: (ordreDeCalcul at: i - 1 +
startQuadrant \\ 4 + 1)].
        (aCalculer includes: #left) ifTrue: [left := left max: (x1 min: x2)].
        (aCalculer includes: #top) ifTrue: [top := top max: (y1 min: y2)].
        (aCalculer includes: #right) ifTrue: [right := right min: (x1 max: x2)].
        (aCalculer includes: #bottom) ifTrue: [bottom := bottom min: (y1 max: y2)].
        ^left @ top corner: right @ bottom




EllipticalArc>>computePoints
        "Answer an array of points which are vertices of a polyline fitted to the
        curve.

        02/03/93 @NC
        Modification méthode SMALLTALK
        1/ sur le nombre de points à calculer
        2/ sur la boucle qui rate parfois un point (arrondis)

        Je donne un rayon R et un écart max. d avec la corde, en pixels,
        J'en déduis l'angle minimum alpha entre deux points.
        cos(alpha/2) = 1 - (d/R)
        mais la formule approchée est aussi bonne pour d<<R:
        alpha = 2*sqrt(2*d/R)

        Pour une ellipse, je prend le max (a max: b) (luxueux...)"

        | left right top bottom a b cx cy nPointsPerQuadrant nStart nStop a3
nStep ws stepAngle |
        left := boundingBox left.
        right := boundingBox right.
        top := boundingBox top.
        bottom := boundingBox bottom.
        a := boundingBox width asFloat / 2.
        b := boundingBox height asFloat / 2.
        cx := boundingBox center x asFloat.
        cy := boundingBox center y asFloat.

"THE FOLLOWING CODE IS CHANGED BY @NC"
        nPointsPerQuadrant := (Float pi * ((a max: b) / 8) sqrt) rounded max:
3. "an Integer such that distance from the circle to the cord between
two points is at most d= 0.25 pixel"
        nStart := nPointsPerQuadrant * startAngle / 90.0.
        nStop := nPointsPerQuadrant * (startAngle+((sweepAngle max: -360.0)
min: 360.0)) / 90.0.
        a3 := Array new: 3.
        (nStart - nStart truncated) isZero ifFalse: [a3 at: 1 put: (Array with:
nStart)].
        (nStop - nStop truncated) isZero ifFalse: [a3 at: 3 put: (Array with:
nStop)].
        sweepAngle >= 0
                ifTrue: [nStart := nStart ceiling. nStop := nStop floor. nStep := 1]
                ifFalse: [nStart := nStart floor. nStop := nStop ceiling. nStep := -1].
        a3 at: 2 put: (nStart to: nStop by: nStep).
        ws := WriteStream on: (Array new: (nStop - nStart) abs + 2).
        stepAngle := Float pi / 2 / nPointsPerQuadrant.
        a3 do: [:a1 | a1 isNil ifFalse: [a1 do: [:i |
                | x y theta |
                theta := stepAngle * i.
                x := a * theta cos + cx.
                y := b * theta sin + cy.
                x < left ifTrue: [x := left].
                x > right ifTrue: [x := right].
                y < top ifTrue: [y := top].
                y > bottom ifTrue: [y := bottom].
                ws nextPut: x@y]]].
        ^ws contents

Reply | Threaded
Open this post in threaded view
|

Re: [Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext

Nicolas Cellier-3
just another point.

It is stupid to compute points rounded to a pixel since we do not even
know the size of the pixel...

GraphicsContext are able of scaling and have their own idea of what is a
pixel (Screen resolution is not PostScript resolution). Only in such a
context can the rounding to pixel make sense.

Nicolas

Reply | Threaded
Open this post in threaded view
|

RE: Re: [Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext

mark.b.ballard
In reply to this post by Nicolas Cellier-3
Here is the solution I came up with for my problem of generating a huge number of points calculated for some EllipticalArc's that were clipped out of the display anyway.  Add a trivial reject in the display method.  Define the EllipticalArc as a very rough polygon to determine if it might intersect the clipping box.  The computePointsForMightIntersects is a copy of the method computePoints, but where the pieces is constrained to be at most 10 per quadrant.  The claim is that if this very rough approximation of the outline of the ellipse does not intersect the clipping box, then the higher resolution one will not either.



1) to this method, add the following trivial reject:

GraphicsContext displayGeneralArcBBoxOrigin:extent:startAngle:sweepAngle: {private-wide lines}

        ((EllipticalArc
                boundingBox: (originPoint extent: extentPoint)
                startAngle: startAngle
                sweepAngle: sweepAngle) outlineMightIntersects: self clippingBounds)
                ifFalse: [^self].

2) Define in class EllipticalArc

outlineMightIntersects: aRectangle
        "Answer whether the receiver's display area intersects aRectangle
        under stroking."

        ^self class vertices: self computePointsForMightIntersects
                intersectsRectangle: aRectangle

computePointsForMightIntersects
        "Answer an array of points which are vertices of a polyline fitted to the
        curve.   Use only 10 points per quadrant for a cheap might intersects test."

        | ws radStart radSweep pieces a b cx cy quadrants left right top bottom step |
        left := boundingBox left.
        right := boundingBox right.
        top := boundingBox top.
        bottom := boundingBox bottom.
        ws := WriteStream on: (Array new: 50).
        a := boundingBox width asFloat / 2.
        b := boundingBox height asFloat / 2.
        cx := boundingBox center x.
        cy := boundingBox center y.
        radStart := startAngle degreesToRadians negated.
        radSweep := ((sweepAngle min: 360.0) max: -360.0) degreesToRadians negated.
        quadrants := (radSweep abs / Float pi * 2.0) rounded max: 1.
        pieces := ((a + b) * radSweep abs / Float pi / 10.0) rounded * quadrants
                                min: quadrants * 10.
        step := radSweep / (pieces max: quadrants).
        "Overshoot the end of the arc to account for roundoff errors"
        (radStart to: radStart + radSweep + (step / 10) by: step) do:
                        [:theta |
                        | x y |
                        x := a * theta cos + cx.
                        y := b * theta sin negated + cy.
                        x < left ifTrue: [x := left].
                        x > right ifTrue: [x := right].
                        y < top ifTrue: [y := top].
                        y > bottom ifTrue: [y := bottom].
                        ws nextPut: x @ y].
        ^ws contents

-----Original Message-----
From: news [mailto:[hidden email]] On Behalf Of nicolas cellier
Sent: Thursday, February 01, 2007 1:11 PM
To: [hidden email]
Subject: Re: [Question][vw7.3.1][BASE][EllipticalArc] -- is there a way to constrain the number of points calculated for the EllipticalArc, by using the clipping box in the graphicsContext


[hidden email] a écrit :

> The following generates a huge amount of points during display (as the
> polygon representation is constructed), but it all gets clipped:
>
>             win := ScheduledWindow new.
> win component: (graphics := CompositePart new).
> win openIn: (30@30 extent: 500@500).
> arc := EllipticalArc boundingBox: (356@61 corner: 3.5d8@3.5d8)
> startAngle: 259 sweepAngle: -80.
> arc displayStrokedOn: win graphicsContext
>
>

I already patched EllipticalArc in ObjectWorks long time ago
- there is no use to computePoint if we just need the bounds
- there are some inaccuracy in computePoints

Unfortunately #outlineIntersects: and #regionIntersects: also call
computePoints to handle clipping.
They would also need a new implementation for obvious economy reasons.

Then your code will invoke #displayArcBoundedBy:startAngle:sweepAngle:
and the primitive will fail i think due to large numbers (most screen
primitive work only with SmallIntegers).

This will try and round the point, fail again, revert to a
#displayGeneralArcBBoxOrigin:extent:startAngle:sweepAngle: that will
compute the points again and invoke primDisplayPolygon: that will fail,
round points, and fail again due to at least two limitations
- small integers
- limited number of points of underlying library (X11 limit is not so
high and you can query it, for Windows i do not know the limit and you
cannot query it AFAIK, for Mac, i do not know)

Obviously, it's a long way before you can display such an Object.

You'll have to solve intersection of clipping rectangle with the ellipse
by yourself (outline and/or region), convert to a polyline (or polygon)
and display the result. Not that simple since there might be multiple
intersections.

Nicolas




EllipticalArc>>computeBounds
        "02/03/93 @NC
        CORRIGE UN BUG SMALLTALK...
        pas la peine de calculer tous les points...
        Les points cardinaux sont #(est nord ouest sud)
        (angles = #(0 pi/2 pi 3*pi/2)
        1/ combien de points cardinaux sont sur l'arc ?
        2/ tient compte du quadrant de départ pour savoir ce qu'il faut calculer"

        | startModulo nCard startQuadrant left right top bottom a b cx cy
radStart radStop x1 y1 x2 y2 ordreDeCalcul aCalculer |
        startModulo := sweepAngle > 0
                                ifTrue: [startAngle - (startAngle // 90 * 90)]
                                ifFalse: [startAngle negated - (startAngle negated // 90 * 90)].
        nCard := sweepAngle abs + startModulo // 90.
        nCard = 4 ifTrue: [^boundingBox].
        startQuadrant := startAngle - (startAngle // 360 * 360) // 90.
        left := boundingBox left.
        right := boundingBox right.
        top := boundingBox top.
        bottom := boundingBox bottom.
        a := boundingBox width asFloat / 2.
        b := boundingBox height asFloat / 2.
        cx := boundingBox center x.
        cy := boundingBox center y.
        radStart := startAngle degreesToRadians.
        radStop := (startAngle + sweepAngle) degreesToRadians.
        x1 := (a * radStart cos + cx max: left) min: right.
        y1 := (b * radStart sin negated + cy max: top) min: bottom.
        x2 := (a * radStop cos + cx max: left) min: right.
        y2 := (b * radStop sin negated + cy max: top) min: bottom.
        ordreDeCalcul := sweepAngle > 0
                                ifTrue: [#(#top #left #bottom #right)]
                                ifFalse: [#(#right #bottom #left #top)].
        aCalculer := Set new: 8.
        nCard + 1 to: 4 do: [:i | aCalculer add: (ordreDeCalcul at: i - 1 +
startQuadrant \\ 4 + 1)].
        (aCalculer includes: #left) ifTrue: [left := left max: (x1 min: x2)].
        (aCalculer includes: #top) ifTrue: [top := top max: (y1 min: y2)].
        (aCalculer includes: #right) ifTrue: [right := right min: (x1 max: x2)].
        (aCalculer includes: #bottom) ifTrue: [bottom := bottom min: (y1 max: y2)].
        ^left @ top corner: right @ bottom




EllipticalArc>>computePoints
        "Answer an array of points which are vertices of a polyline fitted to the
        curve.

        02/03/93 @NC
        Modification méthode SMALLTALK
        1/ sur le nombre de points à calculer
        2/ sur la boucle qui rate parfois un point (arrondis)

        Je donne un rayon R et un écart max. d avec la corde, en pixels,
        J'en déduis l'angle minimum alpha entre deux points.
        cos(alpha/2) = 1 - (d/R)
        mais la formule approchée est aussi bonne pour d<<R:
        alpha = 2*sqrt(2*d/R)

        Pour une ellipse, je prend le max (a max: b) (luxueux...)"

        | left right top bottom a b cx cy nPointsPerQuadrant nStart nStop a3
nStep ws stepAngle |
        left := boundingBox left.
        right := boundingBox right.
        top := boundingBox top.
        bottom := boundingBox bottom.
        a := boundingBox width asFloat / 2.
        b := boundingBox height asFloat / 2.
        cx := boundingBox center x asFloat.
        cy := boundingBox center y asFloat.

"THE FOLLOWING CODE IS CHANGED BY @NC"
        nPointsPerQuadrant := (Float pi * ((a max: b) / 8) sqrt) rounded max:
3. "an Integer such that distance from the circle to the cord between
two points is at most d= 0.25 pixel"
        nStart := nPointsPerQuadrant * startAngle / 90.0.
        nStop := nPointsPerQuadrant * (startAngle+((sweepAngle max: -360.0)
min: 360.0)) / 90.0.
        a3 := Array new: 3.
        (nStart - nStart truncated) isZero ifFalse: [a3 at: 1 put: (Array with:
nStart)].
        (nStop - nStop truncated) isZero ifFalse: [a3 at: 3 put: (Array with:
nStop)].
        sweepAngle >= 0
                ifTrue: [nStart := nStart ceiling. nStop := nStop floor. nStep := 1]
                ifFalse: [nStart := nStart floor. nStop := nStop ceiling. nStep := -1].
        a3 at: 2 put: (nStart to: nStop by: nStep).
        ws := WriteStream on: (Array new: (nStop - nStart) abs + 2).
        stepAngle := Float pi / 2 / nPointsPerQuadrant.
        a3 do: [:a1 | a1 isNil ifFalse: [a1 do: [:i |
                | x y theta |
                theta := stepAngle * i.
                x := a * theta cos + cx.
                y := b * theta sin + cy.
                x < left ifTrue: [x := left].
                x > right ifTrue: [x := right].
                y < top ifTrue: [y := top].
                y > bottom ifTrue: [y := bottom].
                ws nextPut: x@y]]].
        ^ws contents