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 |
[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 |
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 |
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 |
Free forum by Nabble | Edit this page |